Repository navigation
Asset Validation
The Asset Validation system provides a rule-based pipeline for verifying asset health. Built-in rules check texture references in materials, scene cross-references, shader compilability, and metadata completeness. Custom rules are added via the IAssetValidationRule interface.
AssetValidator::Initialize() is called during engine startup (in GameplayLifecycleShared.cpp, alongside the sibling diagnostic singletons), which registers the four built-in rules. Validation is then driven on demand via the assetvalidate.* console commands (see Console Commands); it is well-suited to a pre-packaging or editor-load pass, and Game-Packaging can invoke ValidateDirectory()/ValidateAll() directly.
Source: SparkEngine/Source/Core/AssetValidator.h
| Class | Responsibility |
|---|---|
AssetValidator |
Singleton validation pipeline that runs registered rules against assets; supports single-file, directory, and full-project scans |
IAssetValidationRule |
Abstract interface for implementing custom validation rules |
MaterialTextureValidator |
Built-in rule: checks that .mat files reference textures that exist on disk |
SceneReferenceValidator |
Built-in rule: validates .scene/.scn files for existence, size sanity, and broken references |
ShaderCompilationValidator |
Built-in rule: checks .hlsl/.glsl/.vert/.frag/.comp files for basic syntactic correctness |
AssetMetadataValidator |
Built-in rule: checks for zero-byte files, long filenames, and invalid path characters |
ValidationReport |
Aggregated report from a validation pass with counts and timing |
ValidationResult |
A single finding from one rule against one asset |
Each finding has a severity level that controls how it appears in reports and whether it blocks packaging:
enum class ValidationSeverity : uint8_t
{
Info, // Informational (no action required)
Warning, // Potential issue that may cause runtime problems
Error, // Definite problem that will cause runtime failures
Critical // Blocking issue that prevents asset from being used
};struct ValidationResult
{
ValidationSeverity severity = ValidationSeverity::Info;
std::string assetPath; // Path to the asset that triggered this finding
std::string message; // Human-readable description of the issue
std::string suggestion; // Recommended fix (may be empty)
uint32_t errorCode = 0; // Machine-readable error identifier
};struct ValidationReport
{
std::vector<ValidationResult> results;
uint32_t totalAssets = 0; // Number of assets scanned
uint32_t passCount = 0; // Assets with no errors or warnings
uint32_t failCount = 0; // Assets with at least one Error or Critical
uint32_t warningCount = 0; // Assets with only Warnings (no Error/Critical)
std::string timestamp; // ISO-8601 timestamp of the validation run
float durationMs = 0.0f; // Wall-clock duration in milliseconds
};Scans .mat files for texture path references. Checks that the material file exists and is non-empty, then looks for matching diffuse textures following the naming convention <material>_diffuse.<ext>.
| Error Code | Severity | Condition |
|---|---|---|
| 1001 | Error | Material file does not exist |
| 1002 | Warning | Material file is empty (0 bytes) |
| 1003 | Info | No matching diffuse texture found |
Supported texture extensions: .png, .jpg, .dds, .tga, .bmp
Validates .scene and .scn files for existence and size sanity.
| Error Code | Severity | Condition |
|---|---|---|
| 2001 | Error | Scene file does not exist |
| 2002 | Error | Scene file is empty (0 bytes) |
| 2003 | Warning | Scene file exceeds 100 MB |
Checks shader source files for basic existence and size. This is a fast pre-check; full compilation validation uses the shader compiler.
| Error Code | Severity | Condition |
|---|---|---|
| 3001 | Error | Shader source file does not exist |
| 3002 | Error | Shader source file is empty |
| 3003 | Warning | Shader file is suspiciously small (< 10 bytes) |
Supported extensions: .hlsl, .glsl, .vert, .frag, .comp
Validates general asset health: filename length, path characters, and zero-byte files.
| Error Code | Severity | Condition |
|---|---|---|
| 4001 | Error | Asset file does not exist |
| 4002 | Warning | Filename exceeds 200 characters |
| 4003 | Warning | Path contains problematic characters (#, %, &, {, }) |
| 4004 | Warning | File is empty (0 bytes) |
#include "Core/AssetValidator.h"
void CheckAllAssets()
{
auto& validator = Spark::AssetValidator::GetInstance();
validator.Initialize();
auto report = validator.ValidateAll();
Log::Info("Validation", "Scanned {} assets in {:.1f} ms",
report.totalAssets, report.durationMs);
Log::Info("Validation", "Pass: {}, Fail: {}, Warnings: {}",
report.passCount, report.failCount, report.warningCount);
for (const auto& r : report.results)
{
if (r.severity >= Spark::ValidationSeverity::Error)
Log::Error("Validation", "[E{}] {}: {}", r.errorCode, r.assetPath, r.message);
else if (r.severity == Spark::ValidationSeverity::Warning)
Log::Warn("Validation", "[E{}] {}: {}", r.errorCode, r.assetPath, r.message);
}
}auto report = validator.ValidateFile("Assets/Materials/brick.mat");
if (report.failCount > 0)
Log::Error("Validation", "brick.mat has {} error(s)", report.failCount);auto report = validator.ValidateDirectory("Assets/Levels");
// Only scans files under Assets/Levels/ recursivelyauto rules = validator.GetRegisteredRules();
for (auto name : rules)
Log::Info("Validation", "Rule: {}", name);
// Output:
// Rule: MaterialTextureValidator
// Rule: SceneReferenceValidator
// Rule: ShaderCompilationValidator
// Rule: AssetMetadataValidatorImplement IAssetValidationRule to add project-specific checks:
class TextureSizeValidator final : public Spark::IAssetValidationRule
{
public:
void ValidateAsset(const std::filesystem::path& path,
Spark::ValidationReport& report) override
{
// Only check texture files
auto ext = path.extension().string();
if (ext != ".png" && ext != ".jpg" && ext != ".dds")
return;
namespace fs = std::filesystem;
std::error_code ec;
auto fileSize = fs::file_size(path, ec);
if (ec)
return;
// Warn on textures larger than 16 MB
if (fileSize > 16 * 1024 * 1024)
{
report.results.push_back({
Spark::ValidationSeverity::Warning,
path.string(),
std::format("Texture is {:.1f} MB (recommended max: 16 MB)",
fileSize / (1024.0 * 1024.0)),
"Reduce resolution or use compressed format (DDS/BC7)",
5001
});
}
}
std::string_view GetRuleName() const override
{
return "TextureSizeValidator";
}
};Register it before running validation:
auto& validator = Spark::AssetValidator::GetInstance();
validator.Initialize(); // Registers built-in rules
// Add custom rule
validator.RegisterRule(std::make_unique<TextureSizeValidator>());
// Now ValidateAll() includes your custom rule
auto report = validator.ValidateAll();Use the validator as a pre-packaging gate in your CI pipeline:
auto& validator = Spark::AssetValidator::GetInstance();
validator.Initialize();
auto report = validator.ValidateAll();
// Fail CI if any Error or Critical findings exist
if (report.failCount > 0)
{
Log::Error("CI", "Asset validation failed: {} errors", report.failCount);
for (const auto& r : report.results)
{
if (r.severity >= Spark::ValidationSeverity::Error)
{
Log::Error("CI", " [E{}] {}: {}", r.errorCode, r.assetPath, r.message);
if (!r.suggestion.empty())
Log::Error("CI", " Fix: {}", r.suggestion);
}
}
std::exit(1);
}// Validate first, package only if clean
auto report = validator.ValidateAll();
if (report.failCount > 0)
{
Log::Error("Package", "Cannot package: {} asset error(s)", report.failCount);
return;
}
auto& packager = Spark::GamePackager::GetInstance();
packager.Initialize();
auto result = packager.Package(cfg);| Method | Description |
|---|---|
Console_GetStatus() |
Returns initialization state, rule count, and last run summary |
Console_GetLastReport() |
Returns the full formatted validation report from the last run |
Example Console_GetStatus() output:
AssetValidator: initialized, 4 rule(s) registered
Last run: 312 asset(s) scanned in 45.2 ms
Pass: 298, Fail: 3, Warnings: 11
Example Console_GetLastReport() output:
=== Validation Report (2026-04-04T12:00:00Z) ===
Assets scanned: 312 | Pass: 298 | Fail: 3 | Warnings: 11
Duration: 45.2 ms
[ERROR] Assets/Materials/missing.mat (E1001)
Material file does not exist
Suggestion: Verify the asset path
[WARN] Assets/Levels/huge_level.scene (E2003)
Scene file is very large (142.3 MB)
Suggestion: Consider splitting into streaming sub-scenes
Run migration before validation to ensure assets are in the current format:
auto& migration = Spark::AssetMigrationRegistry::GetInstance();
// Migrate assets first...
auto& validator = Spark::AssetValidator::GetInstance();
auto report = validator.ValidateAll(); // Now validates current-version assetsThe validator pairs naturally with the packaging pipeline as a pre-flight check. See the Game-Packaging wiki page.
The editor can invoke ValidateFile() on individual assets at load time or ValidateDirectory() on the current project to show findings in the editor UI.
| Method | Signature | Description |
|---|---|---|
GetInstance |
static AssetValidator& GetInstance() |
Get the singleton instance |
Initialize |
void Initialize() |
Initialize with built-in rules |
Shutdown |
void Shutdown() |
Release resources and clear rules |
RegisterRule |
void RegisterRule(std::unique_ptr<IAssetValidationRule> rule) |
Register a custom validation rule |
ValidateAll |
ValidationReport ValidateAll() |
Validate all assets under Assets/
|
ValidateFile |
ValidationReport ValidateFile(const std::filesystem::path& path) |
Validate a single file |
ValidateDirectory |
ValidationReport ValidateDirectory(const std::filesystem::path& dir) |
Validate a directory tree recursively |
GetRegisteredRules |
std::vector<std::string_view> GetRegisteredRules() const |
List all registered rule names |
Console_GetStatus |
std::string Console_GetStatus() const |
Human-readable status |
Console_GetLastReport |
std::string Console_GetLastReport() const |
Formatted last report |
| Method | Signature | Description |
|---|---|---|
ValidateAsset |
virtual void ValidateAsset(const std::filesystem::path& path, ValidationReport& report) = 0 |
Validate one asset, appending findings to the report |
GetRuleName |
virtual std::string_view GetRuleName() const = 0 |
Human-readable rule name |
AssetValidator is not thread-safe. All validation methods perform filesystem I/O and modify internal state (m_lastReport). Call from a single thread. The singleton access via GetInstance() is safe (function-local static), but concurrent calls to ValidateAll() or RegisterRule() must be externally synchronized.
Individual IAssetValidationRule implementations should be stateless or internally synchronized if shared across threads.
Registered during engine startup (AssetValidator::RegisterConsoleCommands()), so the pipeline is reachable from the in-engine console:
| Command | Description |
|---|---|
assetvalidate.status |
Show initialization state, registered rule count, and last-run summary |
assetvalidate.report |
Show the full findings (severity, path, message, suggestion) of the last run |
assetvalidate.dir <path> |
Validate every asset under a directory and print a pass/fail/warning summary |
- Game-Packaging -- Package validated assets for distribution
- Asset-Migration -- Migrate asset formats between versions
- Telemetry-System -- Record validation metrics
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers