Migrate builtin shaders to Unity-like authoring

This commit is contained in:
2026-04-04 16:42:31 +08:00
parent 65d55c8536
commit 8d715eb40f
7 changed files with 210 additions and 351 deletions

View File

@@ -475,8 +475,13 @@ Unity-like Shader Authoring (.shader)
- `ShaderLoader` 新增 Unity-like `.shader` authoring 识别与解析入口,但保留现有 JSON manifest `.shader` 兼容路径不动 - `ShaderLoader` 新增 Unity-like `.shader` authoring 识别与解析入口,但保留现有 JSON manifest `.shader` 兼容路径不动
- importer 继续落到现有 runtime shader contract`properties / passes / resources / backend variants` - importer 继续落到现有 runtime shader contract`properties / passes / resources / backend variants`
- `CollectSourceDependencies` 已覆盖新 authoring 路径,`AssetDatabase` 会继续追踪各 backend stage 文件依赖并参与重导入 - `CollectSourceDependencies` 已覆盖新 authoring 路径,`AssetDatabase` 会继续追踪各 backend stage 文件依赖并参与重导入
- 已完成Step 2 `builtin shader 迁到新 authoring 入口`
- `forward-lit / unlit / object-id / depth-only / shadow-caster` 五个 builtin `.shader` 入口已全部切到 Unity-like authoring
- stage 源文件、builtin shader 路径与 renderer 消费 contract 保持不变,迁移只发生在 authoring 入口层
- 补充 `DepthOnly / ShadowCaster` builtin shader loader 覆盖,确保五类 builtin pass 都经过新 authoring 路径验证
- 已验证:`shader_tests` 中新增 authoring 直载与 artifact/reimport 覆盖 - 已验证:`shader_tests` 中新增 authoring 直载与 artifact/reimport 覆盖
- 下一步:进入 Step 2把 builtin shader 逐步迁到新 authoring 入口,并确保 renderer 消费路径与当前 contract 保持一致 - 已验证:`shader_tests` 31/31 通过builtin `ForwardLit / Unlit / ObjectId / DepthOnly / ShadowCaster` 全部通过加载与 backend variant 覆盖
- 下一步:进入 Step 3继续收紧 material 主路径,把 imported shader schema 变成 material 的正式主执行路径
当前阶段明确不做: 当前阶段明确不做:

View File

@@ -1,62 +1,22 @@
Shader "Builtin Depth Only"
{ {
"name": "Builtin Depth Only", SubShader
"passes": [
{ {
"name": "DepthOnly", Pass
"tags": {
"LightMode": "DepthOnly"
},
"resources": [
{ {
"name": "PerObjectConstants", Name "DepthOnly"
"type": "ConstantBuffer", Tags { "LightMode" = "DepthOnly" }
"set": 0, Resources
"binding": 0, {
"semantic": "PerObject" PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
}
HLSLPROGRAM
#pragma vertex MainVS
#pragma fragment MainPS
#pragma backend D3D12 HLSL "depth-only.vs.hlsl" "depth-only.ps.hlsl" vs_5_0 ps_5_0
#pragma backend OpenGL GLSL "depth-only.vert.glsl" "depth-only.frag.glsl"
#pragma backend Vulkan GLSL "depth-only.vert.vk.glsl" "depth-only.frag.vk.glsl"
ENDHLSL
} }
],
"variants": [
{
"stage": "Vertex",
"backend": "D3D12",
"language": "HLSL",
"source": "depth-only.vs.hlsl",
"entryPoint": "MainVS",
"profile": "vs_5_0"
},
{
"stage": "Fragment",
"backend": "D3D12",
"language": "HLSL",
"source": "depth-only.ps.hlsl",
"entryPoint": "MainPS",
"profile": "ps_5_0"
},
{
"stage": "Vertex",
"backend": "OpenGL",
"language": "GLSL",
"source": "depth-only.vert.glsl"
},
{
"stage": "Fragment",
"backend": "OpenGL",
"language": "GLSL",
"source": "depth-only.frag.glsl"
},
{
"stage": "Vertex",
"backend": "Vulkan",
"language": "GLSL",
"source": "depth-only.vert.vk.glsl"
},
{
"stage": "Fragment",
"backend": "Vulkan",
"language": "GLSL",
"source": "depth-only.frag.vk.glsl"
}
]
} }
]
} }

View File

@@ -1,99 +1,30 @@
Shader "Builtin Forward Lit"
{ {
"name": "Builtin Forward Lit", Properties
"properties": [
{ {
"name": "_BaseColor", _BaseColor ("Base Color", Color) = (1,1,1,1) [Semantic(BaseColor)]
"displayName": "Base Color", _MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
"type": "Color",
"defaultValue": "(1,1,1,1)",
"semantic": "BaseColor"
},
{
"name": "_MainTex",
"displayName": "Base Map",
"type": "2D",
"defaultValue": "white",
"semantic": "BaseColorTexture"
} }
], SubShader
"passes": [
{ {
"name": "ForwardLit", Pass
"tags": {
"LightMode": "ForwardBase"
},
"resources": [
{ {
"name": "PerObjectConstants", Name "ForwardLit"
"type": "ConstantBuffer", Tags { "LightMode" = "ForwardBase" }
"set": 1, Resources
"binding": 0, {
"semantic": "PerObject" PerObjectConstants (ConstantBuffer, 1, 0) [Semantic(PerObject)]
}, MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)]
{ BaseColorTexture (Texture2D, 3, 0) [Semantic(BaseColorTexture)]
"name": "MaterialConstants", LinearClampSampler (Sampler, 4, 0) [Semantic(LinearClampSampler)]
"type": "ConstantBuffer", }
"set": 2, HLSLPROGRAM
"binding": 0, #pragma vertex MainVS
"semantic": "Material" #pragma fragment MainPS
}, #pragma backend D3D12 HLSL "forward-lit.vs.hlsl" "forward-lit.ps.hlsl" vs_5_0 ps_5_0
{ #pragma backend OpenGL GLSL "forward-lit.vert.glsl" "forward-lit.frag.glsl"
"name": "BaseColorTexture", #pragma backend Vulkan GLSL "forward-lit.vert.vk.glsl" "forward-lit.frag.vk.glsl"
"type": "Texture2D", ENDHLSL
"set": 3,
"binding": 0,
"semantic": "BaseColorTexture"
},
{
"name": "LinearClampSampler",
"type": "Sampler",
"set": 4,
"binding": 0,
"semantic": "LinearClampSampler"
} }
],
"variants": [
{
"stage": "Vertex",
"backend": "D3D12",
"language": "HLSL",
"source": "forward-lit.vs.hlsl",
"entryPoint": "MainVS",
"profile": "vs_5_0"
},
{
"stage": "Fragment",
"backend": "D3D12",
"language": "HLSL",
"source": "forward-lit.ps.hlsl",
"entryPoint": "MainPS",
"profile": "ps_5_0"
},
{
"stage": "Vertex",
"backend": "OpenGL",
"language": "GLSL",
"source": "forward-lit.vert.glsl"
},
{
"stage": "Fragment",
"backend": "OpenGL",
"language": "GLSL",
"source": "forward-lit.frag.glsl"
},
{
"stage": "Vertex",
"backend": "Vulkan",
"language": "GLSL",
"source": "forward-lit.vert.vk.glsl"
},
{
"stage": "Fragment",
"backend": "Vulkan",
"language": "GLSL",
"source": "forward-lit.frag.vk.glsl"
}
]
} }
]
} }

View File

@@ -1,62 +1,22 @@
Shader "Builtin Object Id"
{ {
"name": "Builtin Object Id", SubShader
"passes": [
{ {
"name": "ObjectId", Pass
"tags": {
"LightMode": "ObjectId"
},
"resources": [
{ {
"name": "PerObjectConstants", Name "ObjectId"
"type": "ConstantBuffer", Tags { "LightMode" = "ObjectId" }
"set": 0, Resources
"binding": 0, {
"semantic": "PerObject" PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
}
HLSLPROGRAM
#pragma vertex MainVS
#pragma fragment MainPS
#pragma backend D3D12 HLSL "object-id.vs.hlsl" "object-id.ps.hlsl" vs_5_0 ps_5_0
#pragma backend OpenGL GLSL "object-id.vert.glsl" "object-id.frag.glsl"
#pragma backend Vulkan GLSL "object-id.vert.vk.glsl" "object-id.frag.vk.glsl"
ENDHLSL
} }
],
"variants": [
{
"stage": "Vertex",
"backend": "D3D12",
"language": "HLSL",
"source": "object-id.vs.hlsl",
"entryPoint": "MainVS",
"profile": "vs_5_0"
},
{
"stage": "Fragment",
"backend": "D3D12",
"language": "HLSL",
"source": "object-id.ps.hlsl",
"entryPoint": "MainPS",
"profile": "ps_5_0"
},
{
"stage": "Vertex",
"backend": "OpenGL",
"language": "GLSL",
"source": "object-id.vert.glsl"
},
{
"stage": "Fragment",
"backend": "OpenGL",
"language": "GLSL",
"source": "object-id.frag.glsl"
},
{
"stage": "Vertex",
"backend": "Vulkan",
"language": "GLSL",
"source": "object-id.vert.vk.glsl"
},
{
"stage": "Fragment",
"backend": "Vulkan",
"language": "GLSL",
"source": "object-id.frag.vk.glsl"
}
]
} }
]
} }

View File

@@ -1,62 +1,22 @@
Shader "Builtin Shadow Caster"
{ {
"name": "Builtin Shadow Caster", SubShader
"passes": [
{ {
"name": "ShadowCaster", Pass
"tags": {
"LightMode": "ShadowCaster"
},
"resources": [
{ {
"name": "PerObjectConstants", Name "ShadowCaster"
"type": "ConstantBuffer", Tags { "LightMode" = "ShadowCaster" }
"set": 0, Resources
"binding": 0, {
"semantic": "PerObject" PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
}
HLSLPROGRAM
#pragma vertex MainVS
#pragma fragment MainPS
#pragma backend D3D12 HLSL "shadow-caster.vs.hlsl" "shadow-caster.ps.hlsl" vs_5_0 ps_5_0
#pragma backend OpenGL GLSL "shadow-caster.vert.glsl" "shadow-caster.frag.glsl"
#pragma backend Vulkan GLSL "shadow-caster.vert.vk.glsl" "shadow-caster.frag.vk.glsl"
ENDHLSL
} }
],
"variants": [
{
"stage": "Vertex",
"backend": "D3D12",
"language": "HLSL",
"source": "shadow-caster.vs.hlsl",
"entryPoint": "MainVS",
"profile": "vs_5_0"
},
{
"stage": "Fragment",
"backend": "D3D12",
"language": "HLSL",
"source": "shadow-caster.ps.hlsl",
"entryPoint": "MainPS",
"profile": "ps_5_0"
},
{
"stage": "Vertex",
"backend": "OpenGL",
"language": "GLSL",
"source": "shadow-caster.vert.glsl"
},
{
"stage": "Fragment",
"backend": "OpenGL",
"language": "GLSL",
"source": "shadow-caster.frag.glsl"
},
{
"stage": "Vertex",
"backend": "Vulkan",
"language": "GLSL",
"source": "shadow-caster.vert.vk.glsl"
},
{
"stage": "Fragment",
"backend": "Vulkan",
"language": "GLSL",
"source": "shadow-caster.frag.vk.glsl"
}
]
} }
]
} }

View File

@@ -1,99 +1,30 @@
Shader "Builtin Unlit"
{ {
"name": "Builtin Unlit", Properties
"properties": [
{ {
"name": "_BaseColor", _BaseColor ("Base Color", Color) = (1,1,1,1) [Semantic(BaseColor)]
"displayName": "Base Color", _MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
"type": "Color",
"defaultValue": "(1,1,1,1)",
"semantic": "BaseColor"
},
{
"name": "_MainTex",
"displayName": "Base Map",
"type": "2D",
"defaultValue": "white",
"semantic": "BaseColorTexture"
} }
], SubShader
"passes": [
{ {
"name": "Unlit", Pass
"tags": {
"LightMode": "Unlit"
},
"resources": [
{ {
"name": "PerObjectConstants", Name "Unlit"
"type": "ConstantBuffer", Tags { "LightMode" = "Unlit" }
"set": 1, Resources
"binding": 0, {
"semantic": "PerObject" PerObjectConstants (ConstantBuffer, 1, 0) [Semantic(PerObject)]
}, MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)]
{ BaseColorTexture (Texture2D, 3, 0) [Semantic(BaseColorTexture)]
"name": "MaterialConstants", LinearClampSampler (Sampler, 4, 0) [Semantic(LinearClampSampler)]
"type": "ConstantBuffer", }
"set": 2, HLSLPROGRAM
"binding": 0, #pragma vertex MainVS
"semantic": "Material" #pragma fragment MainPS
}, #pragma backend D3D12 HLSL "unlit.vs.hlsl" "unlit.ps.hlsl" vs_5_0 ps_5_0
{ #pragma backend OpenGL GLSL "unlit.vert.glsl" "unlit.frag.glsl"
"name": "BaseColorTexture", #pragma backend Vulkan GLSL "unlit.vert.vk.glsl" "unlit.frag.vk.glsl"
"type": "Texture2D", ENDHLSL
"set": 3,
"binding": 0,
"semantic": "BaseColorTexture"
},
{
"name": "LinearClampSampler",
"type": "Sampler",
"set": 4,
"binding": 0,
"semantic": "LinearClampSampler"
} }
],
"variants": [
{
"stage": "Vertex",
"backend": "D3D12",
"language": "HLSL",
"source": "unlit.vs.hlsl",
"entryPoint": "MainVS",
"profile": "vs_5_0"
},
{
"stage": "Fragment",
"backend": "D3D12",
"language": "HLSL",
"source": "unlit.ps.hlsl",
"entryPoint": "MainPS",
"profile": "ps_5_0"
},
{
"stage": "Vertex",
"backend": "OpenGL",
"language": "GLSL",
"source": "unlit.vert.glsl"
},
{
"stage": "Fragment",
"backend": "OpenGL",
"language": "GLSL",
"source": "unlit.frag.glsl"
},
{
"stage": "Vertex",
"backend": "Vulkan",
"language": "GLSL",
"source": "unlit.vert.vk.glsl"
},
{
"stage": "Fragment",
"backend": "Vulkan",
"language": "GLSL",
"source": "unlit.frag.vk.glsl"
}
]
} }
]
} }

View File

@@ -844,6 +844,118 @@ TEST(ShaderLoader, LoadBuiltinObjectIdShaderBuildsBackendVariants) {
delete shader; delete shader;
} }
TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsBackendVariants) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
ASSERT_TRUE(shader->IsValid());
const ShaderPass* pass = shader->FindPass("DepthOnly");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 1u);
EXPECT_EQ(pass->resources[0].semantic, "PerObject");
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[0].set, 0u);
EXPECT_EQ(pass->resources[0].binding, 0u);
ASSERT_EQ(pass->variants.Size(), 6u);
ASSERT_EQ(pass->tags.Size(), 1u);
EXPECT_EQ(pass->tags[0].name, "LightMode");
EXPECT_EQ(pass->tags[0].value, "DepthOnly");
EXPECT_NE(shader->FindVariant("DepthOnly", ShaderType::Vertex, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("DepthOnly", ShaderType::Fragment, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("DepthOnly", ShaderType::Vertex, ShaderBackend::OpenGL), nullptr);
EXPECT_NE(shader->FindVariant("DepthOnly", ShaderType::Fragment, ShaderBackend::OpenGL), nullptr);
EXPECT_NE(shader->FindVariant("DepthOnly", ShaderType::Vertex, ShaderBackend::Vulkan), nullptr);
EXPECT_NE(shader->FindVariant("DepthOnly", ShaderType::Fragment, ShaderBackend::Vulkan), nullptr);
const ShaderStageVariant* d3d12Vertex = shader->FindVariant(
"DepthOnly",
ShaderType::Vertex,
ShaderBackend::D3D12);
ASSERT_NE(d3d12Vertex, nullptr);
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("XC_BUILTIN_DEPTH_ONLY_D3D12_VS"), std::string::npos);
const ShaderStageVariant* openglFragment = shader->FindVariant(
"DepthOnly",
ShaderType::Fragment,
ShaderBackend::OpenGL);
ASSERT_NE(openglFragment, nullptr);
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("XC_BUILTIN_DEPTH_ONLY_OPENGL_PS"), std::string::npos);
const ShaderStageVariant* vulkanFragment = shader->FindVariant(
"DepthOnly",
ShaderType::Fragment,
ShaderBackend::Vulkan);
ASSERT_NE(vulkanFragment, nullptr);
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("XC_BUILTIN_DEPTH_ONLY_VULKAN_PS"), std::string::npos);
delete shader;
}
TEST(ShaderLoader, LoadBuiltinShadowCasterShaderBuildsBackendVariants) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinShadowCasterShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
ASSERT_TRUE(shader->IsValid());
const ShaderPass* pass = shader->FindPass("ShadowCaster");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 1u);
EXPECT_EQ(pass->resources[0].semantic, "PerObject");
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[0].set, 0u);
EXPECT_EQ(pass->resources[0].binding, 0u);
ASSERT_EQ(pass->variants.Size(), 6u);
ASSERT_EQ(pass->tags.Size(), 1u);
EXPECT_EQ(pass->tags[0].name, "LightMode");
EXPECT_EQ(pass->tags[0].value, "ShadowCaster");
EXPECT_NE(shader->FindVariant("ShadowCaster", ShaderType::Vertex, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("ShadowCaster", ShaderType::Fragment, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("ShadowCaster", ShaderType::Vertex, ShaderBackend::OpenGL), nullptr);
EXPECT_NE(shader->FindVariant("ShadowCaster", ShaderType::Fragment, ShaderBackend::OpenGL), nullptr);
EXPECT_NE(shader->FindVariant("ShadowCaster", ShaderType::Vertex, ShaderBackend::Vulkan), nullptr);
EXPECT_NE(shader->FindVariant("ShadowCaster", ShaderType::Fragment, ShaderBackend::Vulkan), nullptr);
const ShaderStageVariant* d3d12Vertex = shader->FindVariant(
"ShadowCaster",
ShaderType::Vertex,
ShaderBackend::D3D12);
ASSERT_NE(d3d12Vertex, nullptr);
EXPECT_NE(
std::string(d3d12Vertex->sourceCode.CStr()).find("XC_BUILTIN_SHADOW_CASTER_D3D12_VS"),
std::string::npos);
const ShaderStageVariant* openglFragment = shader->FindVariant(
"ShadowCaster",
ShaderType::Fragment,
ShaderBackend::OpenGL);
ASSERT_NE(openglFragment, nullptr);
EXPECT_NE(
std::string(openglFragment->sourceCode.CStr()).find("XC_BUILTIN_SHADOW_CASTER_OPENGL_PS"),
std::string::npos);
const ShaderStageVariant* vulkanFragment = shader->FindVariant(
"ShadowCaster",
ShaderType::Fragment,
ShaderBackend::Vulkan);
ASSERT_NE(vulkanFragment, nullptr);
EXPECT_NE(
std::string(vulkanFragment->sourceCode.CStr()).find("XC_BUILTIN_SHADOW_CASTER_VULKAN_PS"),
std::string::npos);
delete shader;
}
TEST(ShaderLoader, ResourceManagerLazilyLoadsBuiltinForwardLitShader) { TEST(ShaderLoader, ResourceManagerLazilyLoadsBuiltinForwardLitShader) {
ResourceManager& manager = ResourceManager::Get(); ResourceManager& manager = ResourceManager::Get();
manager.Shutdown(); manager.Shutdown();