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` 兼容路径不动
- importer 继续落到现有 runtime shader contract`properties / passes / resources / backend variants`
- `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 覆盖
- 下一步:进入 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",
"passes": [
SubShader
{
"name": "DepthOnly",
"tags": {
"LightMode": "DepthOnly"
},
"resources": [
Pass
{
"name": "PerObjectConstants",
"type": "ConstantBuffer",
"set": 0,
"binding": 0,
"semantic": "PerObject"
Name "DepthOnly"
Tags { "LightMode" = "DepthOnly" }
Resources
{
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",
"displayName": "Base Color",
"type": "Color",
"defaultValue": "(1,1,1,1)",
"semantic": "BaseColor"
},
{
"name": "_MainTex",
"displayName": "Base Map",
"type": "2D",
"defaultValue": "white",
"semantic": "BaseColorTexture"
_BaseColor ("Base Color", Color) = (1,1,1,1) [Semantic(BaseColor)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
],
"passes": [
SubShader
{
"name": "ForwardLit",
"tags": {
"LightMode": "ForwardBase"
},
"resources": [
Pass
{
"name": "PerObjectConstants",
"type": "ConstantBuffer",
"set": 1,
"binding": 0,
"semantic": "PerObject"
},
{
"name": "MaterialConstants",
"type": "ConstantBuffer",
"set": 2,
"binding": 0,
"semantic": "Material"
},
{
"name": "BaseColorTexture",
"type": "Texture2D",
"set": 3,
"binding": 0,
"semantic": "BaseColorTexture"
},
{
"name": "LinearClampSampler",
"type": "Sampler",
"set": 4,
"binding": 0,
"semantic": "LinearClampSampler"
Name "ForwardLit"
Tags { "LightMode" = "ForwardBase" }
Resources
{
PerObjectConstants (ConstantBuffer, 1, 0) [Semantic(PerObject)]
MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)]
BaseColorTexture (Texture2D, 3, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 4, 0) [Semantic(LinearClampSampler)]
}
HLSLPROGRAM
#pragma vertex MainVS
#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"
#pragma backend Vulkan GLSL "forward-lit.vert.vk.glsl" "forward-lit.frag.vk.glsl"
ENDHLSL
}
],
"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",
"passes": [
SubShader
{
"name": "ObjectId",
"tags": {
"LightMode": "ObjectId"
},
"resources": [
Pass
{
"name": "PerObjectConstants",
"type": "ConstantBuffer",
"set": 0,
"binding": 0,
"semantic": "PerObject"
Name "ObjectId"
Tags { "LightMode" = "ObjectId" }
Resources
{
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",
"passes": [
SubShader
{
"name": "ShadowCaster",
"tags": {
"LightMode": "ShadowCaster"
},
"resources": [
Pass
{
"name": "PerObjectConstants",
"type": "ConstantBuffer",
"set": 0,
"binding": 0,
"semantic": "PerObject"
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
Resources
{
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",
"displayName": "Base Color",
"type": "Color",
"defaultValue": "(1,1,1,1)",
"semantic": "BaseColor"
},
{
"name": "_MainTex",
"displayName": "Base Map",
"type": "2D",
"defaultValue": "white",
"semantic": "BaseColorTexture"
_BaseColor ("Base Color", Color) = (1,1,1,1) [Semantic(BaseColor)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
],
"passes": [
SubShader
{
"name": "Unlit",
"tags": {
"LightMode": "Unlit"
},
"resources": [
Pass
{
"name": "PerObjectConstants",
"type": "ConstantBuffer",
"set": 1,
"binding": 0,
"semantic": "PerObject"
},
{
"name": "MaterialConstants",
"type": "ConstantBuffer",
"set": 2,
"binding": 0,
"semantic": "Material"
},
{
"name": "BaseColorTexture",
"type": "Texture2D",
"set": 3,
"binding": 0,
"semantic": "BaseColorTexture"
},
{
"name": "LinearClampSampler",
"type": "Sampler",
"set": 4,
"binding": 0,
"semantic": "LinearClampSampler"
Name "Unlit"
Tags { "LightMode" = "Unlit" }
Resources
{
PerObjectConstants (ConstantBuffer, 1, 0) [Semantic(PerObject)]
MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)]
BaseColorTexture (Texture2D, 3, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 4, 0) [Semantic(LinearClampSampler)]
}
HLSLPROGRAM
#pragma vertex MainVS
#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"
#pragma backend Vulkan GLSL "unlit.vert.vk.glsl" "unlit.frag.vk.glsl"
ENDHLSL
}
],
"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;
}
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) {
ResourceManager& manager = ResourceManager::Get();
manager.Shutdown();