Add shader artifact import pipeline

This commit is contained in:
2026-04-03 14:56:51 +08:00
parent 0f51f553c8
commit d4afa022c1
8 changed files with 1095 additions and 4 deletions

View File

@@ -4,7 +4,7 @@
#include <XCEngine/Debug/Logger.h>
#include <XCEngine/Resources/Material/MaterialLoader.h>
#include <XCEngine/Resources/Mesh/MeshLoader.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include <XCEngine/Resources/Shader/ShaderLoader.h>
#include <XCEngine/Resources/Texture/TextureLoader.h>
#include <algorithm>
@@ -406,6 +406,81 @@ bool WriteMaterialArtifactFile(
return static_cast<bool>(output);
}
bool WriteShaderArtifactFile(const fs::path& artifactPath, const Shader& shader) {
std::ofstream output(artifactPath, std::ios::binary | std::ios::trunc);
if (!output.is_open()) {
return false;
}
ShaderArtifactFileHeader fileHeader;
output.write(reinterpret_cast<const char*>(&fileHeader), sizeof(fileHeader));
WriteString(output, shader.GetName());
WriteString(output, NormalizeArtifactPathString(shader.GetPath()));
ShaderArtifactHeader header;
header.propertyCount = static_cast<Core::uint32>(shader.GetProperties().Size());
header.passCount = shader.GetPassCount();
output.write(reinterpret_cast<const char*>(&header), sizeof(header));
for (const ShaderPropertyDesc& property : shader.GetProperties()) {
WriteString(output, property.name);
WriteString(output, property.displayName);
WriteString(output, property.defaultValue);
WriteString(output, property.semantic);
ShaderPropertyArtifact propertyArtifact;
propertyArtifact.propertyType = static_cast<Core::uint32>(property.type);
output.write(reinterpret_cast<const char*>(&propertyArtifact), sizeof(propertyArtifact));
}
for (const ShaderPass& pass : shader.GetPasses()) {
WriteString(output, pass.name);
ShaderPassArtifactHeader passHeader;
passHeader.tagCount = static_cast<Core::uint32>(pass.tags.Size());
passHeader.resourceCount = static_cast<Core::uint32>(pass.resources.Size());
passHeader.variantCount = static_cast<Core::uint32>(pass.variants.Size());
output.write(reinterpret_cast<const char*>(&passHeader), sizeof(passHeader));
for (const ShaderPassTagEntry& tag : pass.tags) {
WriteString(output, tag.name);
WriteString(output, tag.value);
}
for (const ShaderResourceBindingDesc& resource : pass.resources) {
WriteString(output, resource.name);
WriteString(output, resource.semantic);
ShaderResourceArtifact resourceArtifact;
resourceArtifact.resourceType = static_cast<Core::uint32>(resource.type);
resourceArtifact.set = resource.set;
resourceArtifact.binding = resource.binding;
output.write(reinterpret_cast<const char*>(&resourceArtifact), sizeof(resourceArtifact));
}
for (const ShaderStageVariant& variant : pass.variants) {
ShaderVariantArtifactHeader variantHeader;
variantHeader.stage = static_cast<Core::uint32>(variant.stage);
variantHeader.language = static_cast<Core::uint32>(variant.language);
variantHeader.backend = static_cast<Core::uint32>(variant.backend);
variantHeader.compiledBinarySize = static_cast<Core::uint64>(variant.compiledBinary.Size());
output.write(reinterpret_cast<const char*>(&variantHeader), sizeof(variantHeader));
WriteString(output, variant.entryPoint);
WriteString(output, variant.profile);
WriteString(output, variant.sourceCode);
if (!variant.compiledBinary.Empty()) {
output.write(
reinterpret_cast<const char*>(variant.compiledBinary.Data()),
static_cast<std::streamsize>(variant.compiledBinary.Size()));
}
}
}
return static_cast<bool>(output);
}
bool WriteMeshArtifactFile(const fs::path& artifactPath,
const Mesh& mesh,
const std::vector<Containers::String>& materialArtifactPaths) {
@@ -489,6 +564,7 @@ void AssetDatabase::Initialize(const Containers::String& projectRoot) {
LoadSourceAssetDB();
LoadArtifactDB();
ScanAssets();
SaveArtifactDB();
}
void AssetDatabase::Shutdown() {
@@ -996,6 +1072,10 @@ Containers::String AssetDatabase::GetImporterNameForPath(const Containers::Strin
if (ext == ".obj" || ext == ".fbx" || ext == ".gltf" || ext == ".glb" || ext == ".dae" || ext == ".stl") {
return Containers::String("ModelImporter");
}
if (ext == ".shader" || ext == ".hlsl" || ext == ".glsl" || ext == ".vert" || ext == ".frag" ||
ext == ".geom" || ext == ".comp") {
return Containers::String("ShaderImporter");
}
if (ext == ".mat" || ext == ".material" || ext == ".json") {
return Containers::String("MaterialImporter");
}
@@ -1012,6 +1092,9 @@ ResourceType AssetDatabase::GetPrimaryResourceTypeForImporter(const Containers::
if (importerName == "MaterialImporter") {
return ResourceType::Material;
}
if (importerName == "ShaderImporter") {
return ResourceType::Shader;
}
return ResourceType::Unknown;
}
@@ -1049,6 +1132,8 @@ bool AssetDatabase::ImportAsset(const SourceAssetRecord& sourceRecord,
return ImportMaterialAsset(sourceRecord, outRecord);
case ResourceType::Mesh:
return ImportModelAsset(sourceRecord, outRecord);
case ResourceType::Shader:
return ImportShaderAsset(sourceRecord, outRecord);
default:
return false;
}
@@ -1362,6 +1447,59 @@ bool AssetDatabase::ImportModelAsset(const SourceAssetRecord& sourceRecord,
return true;
}
bool AssetDatabase::ImportShaderAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord) {
ShaderLoader loader;
const Containers::String absolutePath =
NormalizePathString(fs::path(m_projectRoot.CStr()) / sourceRecord.relativePath.CStr());
LoadResult result = loader.Load(absolutePath);
if (!result || result.resource == nullptr) {
return false;
}
Shader* shader = static_cast<Shader*>(result.resource);
std::vector<ArtifactDependencyRecord> dependencies;
if (!CollectShaderDependencies(sourceRecord, dependencies)) {
delete shader;
return false;
}
const Containers::String artifactKey = BuildArtifactKey(sourceRecord, dependencies);
const Containers::String artifactDir = BuildArtifactDirectory(artifactKey);
const Containers::String mainArtifactPath =
NormalizePathString(fs::path(artifactDir.CStr()) / "main.xcshader");
std::error_code ec;
fs::remove_all(fs::path(m_projectRoot.CStr()) / artifactDir.CStr(), ec);
ec.clear();
fs::create_directories(fs::path(m_projectRoot.CStr()) / artifactDir.CStr(), ec);
if (ec) {
delete shader;
return false;
}
const bool writeOk =
WriteShaderArtifactFile(fs::path(m_projectRoot.CStr()) / mainArtifactPath.CStr(), *shader);
delete shader;
if (!writeOk) {
return false;
}
outRecord.artifactKey = artifactKey;
outRecord.assetGuid = sourceRecord.guid;
outRecord.importerName = sourceRecord.importerName;
outRecord.importerVersion = sourceRecord.importerVersion;
outRecord.resourceType = ResourceType::Shader;
outRecord.artifactDirectory = artifactDir;
outRecord.mainArtifactPath = mainArtifactPath;
outRecord.sourceHash = sourceRecord.sourceHash;
outRecord.metaHash = sourceRecord.metaHash;
outRecord.sourceFileSize = sourceRecord.sourceFileSize;
outRecord.sourceWriteTime = sourceRecord.sourceWriteTime;
outRecord.mainLocalID = kMainAssetLocalID;
outRecord.dependencies = std::move(dependencies);
return true;
}
Containers::String AssetDatabase::BuildArtifactKey(
const AssetDatabase::SourceAssetRecord& sourceRecord,
const std::vector<AssetDatabase::ArtifactDependencyRecord>& dependencies) const {
@@ -1528,6 +1666,19 @@ bool AssetDatabase::CollectMaterialDependencies(
outDependencies.clear();
std::unordered_set<std::string> seenDependencyPaths;
if (material.GetShader() != nullptr) {
const Containers::String shaderPath = material.GetShader()->GetPath();
if (!shaderPath.Empty() && !HasVirtualPathScheme(shaderPath)) {
ArtifactDependencyRecord dependency;
if (CaptureDependencyRecord(ResolveDependencyPath(shaderPath), dependency)) {
const std::string dependencyKey = ToStdString(dependency.path);
if (seenDependencyPaths.insert(dependencyKey).second) {
outDependencies.push_back(dependency);
}
}
}
}
for (Core::uint32 bindingIndex = 0; bindingIndex < material.GetTextureBindingCount(); ++bindingIndex) {
const Containers::String texturePath = material.GetTextureBindingPath(bindingIndex);
if (texturePath.Empty()) {
@@ -1548,6 +1699,39 @@ bool AssetDatabase::CollectMaterialDependencies(
return true;
}
bool AssetDatabase::CollectShaderDependencies(
const SourceAssetRecord& sourceRecord,
std::vector<AssetDatabase::ArtifactDependencyRecord>& outDependencies) const {
outDependencies.clear();
ShaderLoader loader;
const Containers::String absolutePath =
NormalizePathString(fs::path(m_projectRoot.CStr()) / sourceRecord.relativePath.CStr());
Containers::Array<Containers::String> dependencyPaths;
if (!loader.CollectSourceDependencies(absolutePath, dependencyPaths)) {
return false;
}
std::unordered_set<std::string> seenDependencyPaths;
for (const Containers::String& dependencyPath : dependencyPaths) {
if (dependencyPath.Empty() || HasVirtualPathScheme(dependencyPath)) {
continue;
}
ArtifactDependencyRecord dependency;
if (!CaptureDependencyRecord(ResolveDependencyPath(dependencyPath), dependency)) {
continue;
}
const std::string dependencyKey = ToStdString(dependency.path);
if (seenDependencyPaths.insert(dependencyKey).second) {
outDependencies.push_back(dependency);
}
}
return true;
}
Containers::String AssetDatabase::BuildArtifactDirectory(const Containers::String& artifactKey) const {
if (artifactKey.Length() < 2) {
return Containers::String("Library/Artifacts/00/invalid");