Practice/Roblox/Design a Resource Loader in a Game Engine
Design a Resource Loader in a Game Engine
System DesignMust
Problem Statement
Design a resource loading system for a game engine that can load assets (textures, meshes, audio files, shaders, and scripts) from disk or network into memory efficiently. The loader must support asynchronous loading so the game does not freeze while assets are being fetched, handle dependencies between resources (a character model depends on its textures and animations), manage memory by evicting unused resources when limits are reached, and support hot-reloading during development so artists and designers can see changes without restarting the game.
This is a systems design question rooted in game engine architecture rather than distributed web services. At Roblox, where millions of user-created experiences load diverse assets dynamically, resource loading performance directly impacts player experience. A poorly designed loader causes visible pop-in, stuttering during gameplay, excessive memory usage leading to crashes on lower-end devices, and slow iteration cycles for content creators. The challenge is balancing loading speed, memory efficiency, and developer ergonomics within the constraints of real-time rendering (assets must be available before the frame that needs them).
Interviewers use this question to evaluate your understanding of concurrent programming patterns, dependency graph resolution, cache eviction policies, and the unique constraints of real-time systems where latency spikes of even 16ms (one frame at 60 FPS) are visible to users.
Key Requirements
Functional
- Asynchronous loading -- load assets in background threads without blocking the main game loop; provide callbacks or futures to notify when assets are ready
- Dependency resolution -- automatically identify and load transitive dependencies (loading a scene loads its models, which load their textures and shaders)
- Memory management -- enforce a configurable memory budget, evicting least-recently-used assets when the budget is exceeded, and supporting priority levels to keep critical assets pinned
- Hot-reloading -- detect file changes on disk during development and reload modified assets without restarting the game, updating all references to the old asset
Non-Functional
- Performance -- loading common asset types (textures, meshes) should complete within 50ms for cached assets and under 500ms for cold loads from network; never block the main thread for more than 1ms
- Scalability -- handle scenes with 10,000+ distinct assets and dependency graphs with hundreds of nodes without performance degradation
- Reliability -- gracefully handle missing or corrupted assets by substituting placeholders rather than crashing; retry network fetches with exponential backoff
- Memory efficiency -- support devices with as little as 2GB RAM by aggressively managing the asset cache and supporting streaming for large assets
What Interviewers Focus On
Based on real interview experiences, these are the areas interviewers probe most deeply:
1. Async Loading Architecture and Thread Safety
The main game loop runs at 60 FPS, meaning you have roughly 16ms per frame. Any blocking I/O on the main thread causes visible stuttering. Interviewers want to see how you design a loading pipeline that is fully asynchronous while safely delivering results to the main thread.
Hints to consider:
- Use a thread pool for I/O-bound operations (disk reads, network fetches) and a separate pool for CPU-bound operations (decompression, texture format conversion)
- Deliver loaded assets to the main thread through a lock-free queue that the main loop drains each frame, avoiding synchronization overhead
- Implement a future/promise pattern where game code requests an asset and receives a handle that transitions from "loading" to "ready" without polling
- Consider prioritization: assets needed for the current frame should preempt assets being speculatively prefetched for the next area
2. Dependency Graph Resolution
Real game assets form complex dependency graphs. A scene file references models, which reference materials, which reference textures and shaders. Loading must traverse these dependencies efficiently without redundant work.
Hints to consider:
- Parse asset metadata (headers or manifest files) to build a dependency graph before initiating any I/O, enabling batch scheduling
- Use topological sorting to determine the optimal loading order, ensuring dependencies are loaded before dependents
- Detect circular dependencies at load time and break cycles by deferring one edge (e.g., load a placeholder texture and swap it later)
- Deduplicate shared dependencies: if two models reference the same texture, load it once and share the reference
3. Memory Management and Cache Eviction
Games run on devices with limited memory, and loading new assets may require evicting old ones. Interviewers probe how you choose what to evict and how you handle the complexity of assets that are referenced by multiple consumers.
Hints to consider:
- Implement reference counting combined with LRU eviction: assets with zero references are eligible for eviction, prioritized by last access time
- Support priority tiers (critical, normal, speculative) where critical assets (player character, UI) are never evicted and speculative prefetches are evicted first
- Track memory usage per asset type and enforce per-category budgets (e.g., textures get 60% of budget, audio gets 20%) to prevent one category from starving others
- Consider streaming large assets (terrain textures, audio) in chunks rather than loading them entirely into memory
4. Hot-Reloading for Development Workflows
During development, artists and designers modify assets frequently. Hot-reloading reduces iteration time from minutes to seconds, making it a critical feature for game studios.
Hints to consider:
- Use file system watchers (inotify on Linux, FSEvents on macOS) to detect file modifications and trigger reload of the changed asset
- Maintain a mapping from file paths to loaded asset handles so that when a file changes, you know exactly which in-memory asset to replace
- Swap the underlying data behind the asset handle atomically (pointer swap) so all game objects referencing the asset automatically see the updated version
- Reload the dependency chain: if a shader file changes, reload the shader, then invalidate and reload all materials that use it
Suggested Approach
Step 1: Clarify Requirements
Ask the interviewer about the target platforms (PC, mobile, console?) and their memory constraints. Clarify the asset types in scope: just textures and meshes, or also audio, scripts, and animations? Ask whether assets come from local disk only or also from a network server (as in Roblox, where user-generated content is downloaded on demand). Confirm whether the loader needs to support streaming (progressive loading of large assets) or if assets are all-or-nothing. Ask about the build pipeline: are assets preprocessed into optimized formats, or does the loader need to handle raw formats? Clarify the hot-reload requirement: is this development-only, or should live games support updating assets without restart?