This commit is contained in:
2026-01-26 22:55:14 +09:00
parent 8d82c0a750
commit b505c7c1c0
8 changed files with 97 additions and 9 deletions

View File

@@ -3,5 +3,7 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.Editor")] [assembly: InternalsVisibleTo("Ghost.Editor")]
[assembly: InternalsVisibleTo("Ghost.Editor.Core")] [assembly: InternalsVisibleTo("Ghost.Editor.Core")]
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
[assembly: InternalsVisibleTo("Ghost.MicroTest")]
[assembly: EngineAssembly] [assembly: EngineAssembly]

View File

@@ -7,23 +7,26 @@ This document outlines the plan for implementing the AssetDB, including its stru
- Asset Metadata: Each asset will have associated metadata, including: - Asset Metadata: Each asset will have associated metadata, including:
- Unique Identifier (GUID) - Unique Identifier (GUID)
- Version - Version (Version of the asset pipeline, not the asset. This is primarily for migration when we redesign the asset pipeline in the future)
- Tags
- Importer Settings - Importer Settings
An example of metadata file (filename.png.gmeta): An simplified example of metadata file (filename.png.gmeta):
```json ```json
{ {
"Guid": "123e4567-e89b-12d3-a456-426614174000", "Guid": "123e4567-e89b-12d3-a456-426614174000",
"Version": 1, "Version": 1,
"ImporterSettings": { "Tags": ["Environment", "Texture"],
"ImporterSettings": [
"TextureImporter": { "TextureImporter": {
"Version": 1,
"MaxSize": 2048, "MaxSize": 2048,
"MipLevels": 1 "MipLevels": 1
}, },
"OtherImporter": { "OtherImporter": {
} }
} ]
} }
``` ```
@@ -38,7 +41,19 @@ An example of metadata file (filename.png.gmeta):
- Automatic asset re-importing when source files change. - Automatic asset re-importing when source files change.
- Asset tagging. - Asset tagging.
- Add type specific default importer settings for new asset. - Add type specific default importer settings for new asset.
- SQLite for persistent storage and efficient querying. (Don't use JSON or XML for the database itself, only for metadata files. Also don't use heavy) - SQLite (`Microsoft.Data.Sqlite`) for persistent storage and efficient querying.
An simplified data model example in SQLite:
```sql
CREATE TABLE Assets (
Guid TEXT PRIMARY KEY,
Path TEXT NOT NULL,
Type INTEGER,
Version INTEGER,
Tags TEXT,
DependencyGuids TEXT
);
```
## Simplified Workflow ## Simplified Workflow
@@ -52,6 +67,8 @@ An example of metadata file (filename.png.gmeta):
1. A file is removed from the project directory. 1. A file is removed from the project directory.
2. Deletes the corresponding asset metadata. 2. Deletes the corresponding asset metadata.
3. Remove the asset from the database.
4. Mark dependent assets as dirty for re-importing.
### File Renaming/Moving ### File Renaming/Moving
@@ -61,6 +78,19 @@ An example of metadata file (filename.png.gmeta):
- if not, regenerate the metadata file for the new path and update the database. - if not, regenerate the metadata file for the new path and update the database.
3. Delete the old metadata file if exists. 3. Delete the old metadata file if exists.
### File Modification
1. A file is modified in the project directory.
2. Check the file hash to see if it has changed.
- If changed, mark the asset as dirty for re-importing.
- If not, do nothing.
### Asset Importing (You don't need to write any assets importer right now, just write the framework and a simple test importer if it's needed for unit test)
1. An asset is marked as dirty.
2. The asset importer for that type processes the asset based on its importer settings.
3. Validate the references and dependencies, report errors if any (for example, missing dependencies).
## Features Checklist ## Features Checklist
### In Code (API) ### In Code (API)
@@ -86,3 +116,19 @@ An example of metadata file (filename.png.gmeta):
- [ ] Automatic asset re-importing when source files change (detect changes via file system watcher and quick hash comparison). - [ ] Automatic asset re-importing when source files change (detect changes via file system watcher and quick hash comparison).
- [ ] Asset dependency management. - [ ] Asset dependency management.
- [ ] Validate and fix AssetDB on project load (check for missing/corrupted assets if user add/delete/rename/move files when the editor is not running, etc.) - [ ] Validate and fix AssetDB on project load (check for missing/corrupted assets if user add/delete/rename/move files when the editor is not running, etc.)
- [ ] Asset importer system to handle different asset types and their import settings.
## Testing
Make sure everything builds correctly at first.
Write unit tests and integration tests to ensure the AssetDB functions correctly inside the `Ghost.UnitTests` project.
## Critical Considerations
- Performance: Ensure that the AssetDB operations are efficient, especially for large projects with many assets.
- Stability: The meta data files should be the only source of truth for asset information.
The AssetDB should be able to recover from inconsistencies by re-generating data from the meta files.
We still need to trust the meta data files even if they are corrupted or missing by regenerating them when necessary.
Database is used for caching and quick lookup only.
- Asynchronous patterns: Consider using asynchronous operations for file I/O and database operations to avoid blocking the main thread.
Packages like `Microsoft.Data.Sqlite` support async operations.

View File

@@ -7,7 +7,7 @@ namespace Ghost.Editor.Core.AssetHandle;
public static partial class AssetDatabase public static partial class AssetDatabase
{ {
private static readonly Dictionary<string, Action<string>> _assetOpenHandlers = new(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Action<string>> s_assetOpenHandlers = new(StringComparer.OrdinalIgnoreCase);
private static void InitializeAssetHandle() private static void InitializeAssetHandle()
{ {
@@ -23,12 +23,12 @@ public static partial class AssetDatabase
var del = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), method); var del = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), method);
foreach (var ext in attr.Extensions) foreach (var ext in attr.Extensions)
{ {
if (_assetOpenHandlers.ContainsKey(ext)) if (s_assetOpenHandlers.ContainsKey(ext))
{ {
Logger.LogError($"Duplicate asset open handler for extension '{ext}' found in method '{method.Name}'. Existing handler will be overwritten."); Logger.LogError($"Duplicate asset open handler for extension '{ext}' found in method '{method.Name}'. Existing handler will be overwritten.");
} }
_assetOpenHandlers[ext] = del; s_assetOpenHandlers[ext] = del;
} }
} }
} }
@@ -36,7 +36,7 @@ public static partial class AssetDatabase
public static void OpenAsset(string path) public static void OpenAsset(string path)
{ {
var extension = Path.GetExtension(path); var extension = Path.GetExtension(path);
if (_assetOpenHandlers.TryGetValue(extension, out var handler)) if (s_assetOpenHandlers.TryGetValue(extension, out var handler))
{ {
handler(path); handler(path);
} }

View File

@@ -13,6 +13,7 @@
<langversion>preview</langversion> <langversion>preview</langversion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.2" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7463" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7463" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260101001" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260101001" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0-windows10.0.22621.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64;x86;ARM64</Platforms>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.2" />
<PackageReference Include="MSTest" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ghost.Editor.Core\Ghost.Editor.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1 @@
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]

10
Ghost.UnitTest/Test1.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace Ghost.UnitTest;
[TestClass]
public sealed class Test1
{
[TestMethod]
public void TestMethod1()
{
}
}

View File

@@ -36,6 +36,9 @@
<Project Path="Ghost.MicroTest/Ghost.MicroTest.csproj" Id="8c8ffa4b-e1e4-46a1-9221-7b508a109edd" /> <Project Path="Ghost.MicroTest/Ghost.MicroTest.csproj" Id="8c8ffa4b-e1e4-46a1-9221-7b508a109edd" />
<Project Path="Ghost.Shader.Test/Ghost.Shader.Test.csproj" /> <Project Path="Ghost.Shader.Test/Ghost.Shader.Test.csproj" />
<Project Path="Ghost.Test.Core/Ghost.Test.Core.csproj" /> <Project Path="Ghost.Test.Core/Ghost.Test.Core.csproj" />
<Project Path="Ghost.UnitTest/Ghost.UnitTest.csproj" Id="4da45668-456b-4dcc-acd8-6bfe154e6837">
<Platform Solution="Debug|x64" Project="x64" />
</Project>
</Folder> </Folder>
<Project Path="Ghost.DSL/Ghost.DSL.csproj" /> <Project Path="Ghost.DSL/Ghost.DSL.csproj" />
</Solution> </Solution>