rendering: unify builtin forward and depth-style shaders

This commit is contained in:
2026-04-07 03:35:06 +08:00
parent 503ffbc4ff
commit 5f9f3386ab
27 changed files with 1135 additions and 1151 deletions

View File

@@ -1,20 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_OPENGL_PS
#version 430
layout(binding = 0) uniform sampler2D uBaseColorTexture;
layout(std140, binding = 1) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
in vec2 vTexCoord;
void main() {
#ifdef XC_ALPHA_TEST
vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor;
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
}

View File

@@ -1,21 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_VULKAN_PS
#version 450
layout(set = 2, binding = 0) uniform texture2D uBaseColorTexture;
layout(set = 3, binding = 0) uniform sampler uLinearSampler;
layout(set = 1, binding = 0, std140) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
layout(location = 0) in vec2 vTexCoord;
void main() {
#ifdef XC_ALPHA_TEST
vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor;
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
}

View File

@@ -1,20 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_D3D12_PS
Texture2D gBaseColorTexture : register(t0);
SamplerState gLinearSampler : register(s0);
cbuffer MaterialConstants : register(b1) {
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
struct PSInput {
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
void MainPS(PSInput input) {
#ifdef XC_ALPHA_TEST
float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
}

View File

@@ -6,26 +6,67 @@ Shader "Builtin Depth Only"
_Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
HLSLINCLUDE
cbuffer PerObjectConstants : register(b0)
{
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
cbuffer MaterialConstants : register(b1)
{
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
Texture2D BaseColorTexture : register(t0);
SamplerState LinearClampSampler : register(s0);
struct VSInput
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input)
{
PSInput output;
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
const float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}
float MainPS(PSInput input) : SV_Depth
{
#ifdef XC_ALPHA_TEST
const float4 baseColor =
BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
return input.position.z;
}
ENDHLSL
SubShader
{
Pass
{
Name "DepthOnly"
Tags { "LightMode" = "DepthOnly" }
Resources
{
PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
MaterialConstants (ConstantBuffer, 1, 0) [Semantic(Material)]
BaseColorTexture (Texture2D, 2, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 3, 0) [Semantic(LinearClampSampler)]
}
HLSLPROGRAM
#pragma target 4.5
#pragma vertex MainVS
#pragma fragment MainPS
#pragma shader_feature_local _ XC_ALPHA_TEST
#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
}
}

View File

@@ -1,19 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_OPENGL_VS
#version 430
layout(location = 0) in vec3 aPosition;
layout(location = 2) in vec2 aTexCoord;
layout(std140, binding = 0) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,19 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_VULKAN_VS
#version 450
layout(location = 0) in vec3 aPosition;
layout(location = 2) in vec2 aTexCoord;
layout(set = 0, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
layout(location = 0) out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,25 +0,0 @@
// XC_BUILTIN_DEPTH_ONLY_D3D12_VS
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
struct VSInput {
float3 position : POSITION;
float2 texcoord : TEXCOORD0;
};
struct PSInput {
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input) {
PSInput output;
float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0));
float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}

View File

@@ -1,174 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_OPENGL_PS
#version 430
layout(binding = 0) uniform sampler2D uBaseColorTexture;
layout(binding = 1) uniform sampler2D uShadowMapTexture;
layout(std140, binding = 0) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
mat4 gNormalMatrix;
};
const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData {
vec4 colorAndIntensity;
vec4 positionAndRange;
vec4 directionAndType;
vec4 spotAnglesAndFlags;
};
layout(std140, binding = 1) uniform LightingConstants {
vec4 gMainLightDirectionAndIntensity;
vec4 gMainLightColorAndFlags;
vec4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
layout(std140, binding = 2) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
layout(std140, binding = 3) uniform ShadowReceiverConstants {
mat4 gWorldToShadowMatrix;
vec4 gShadowBiasAndTexelSize;
vec4 gShadowOptions;
};
in vec3 vNormalWS;
in vec2 vTexCoord;
in vec3 vPositionWS;
layout(location = 0) out vec4 fragColor;
float ComputeShadowAttenuation(vec3 positionWS) {
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0;
#else
if (gShadowOptions.x < 0.5) {
return 1.0;
}
vec4 shadowClip = gWorldToShadowMatrix * vec4(positionWS, 1.0);
if (shadowClip.w <= 0.0) {
return 1.0;
}
vec3 shadowNdc = shadowClip.xyz / shadowClip.w;
vec2 shadowUv = vec2(
shadowNdc.x * 0.5 + 0.5,
shadowNdc.y * 0.5 + 0.5);
if (shadowUv.x < 0.0 || shadowUv.x > 1.0 ||
shadowUv.y < 0.0 || shadowUv.y > 1.0 ||
shadowNdc.z < -1.0 || shadowNdc.z > 1.0) {
return 1.0;
}
float shadowDepth = texture(uShadowMapTexture, shadowUv).r;
float receiverDepth = shadowNdc.z * 0.5 + 0.5 - gShadowBiasAndTexelSize.x;
float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0);
return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range) {
if (range <= 0.0) {
return 0.0;
}
float clampedRange = max(range, 0.0001);
float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0;
}
float distance = sqrt(max(distanceSq, 0.0));
float normalized = clamp(1.0 - distance / clampedRange, 0.0, 1.0);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, vec3 directionToLightWS) {
float cosOuter = light.spotAnglesAndFlags.x;
float cosInner = light.spotAnglesAndFlags.y;
vec3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return clamp((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4), 0.0, 1.0);
}
vec3 EvaluateAdditionalLight(AdditionalLightData light, vec3 normalWS, vec3 positionWS) {
float lightType = light.directionAndType.w;
vec3 lightColor = light.colorAndIntensity.rgb;
float lightIntensity = light.colorAndIntensity.w;
vec3 directionToLightWS = vec3(0.0);
float attenuation = 1.0;
if (lightType < 0.5) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
vec3 lightVectorWS = light.positionAndRange.xyz - positionWS;
float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6) {
return vec3(0.0);
}
directionToLightWS = normalize(lightVectorWS);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0) {
return vec3(0.0);
}
if (lightType > 1.5) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0) {
return vec3(0.0);
}
}
}
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
if (diffuse <= 0.0) {
return vec3(0.0);
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
void main() {
vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor;
#ifdef XC_ALPHA_TEST
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
int additionalLightCount = min(int(gLightingParams.x + 0.5), XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.w < 0.5 && additionalLightCount == 0) {
fragColor = baseColor;
return;
}
vec3 normalWS = normalize(vNormalWS);
vec3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.w >= 0.5) {
vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
float shadowAttenuation = diffuse > 0.0
? ComputeShadowAttenuation(vPositionWS)
: 1.0;
lighting +=
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, vPositionWS);
}
fragColor = vec4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -1,176 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_VULKAN_PS
#version 450
layout(set = 4, binding = 0) uniform texture2D uBaseColorTexture;
layout(set = 5, binding = 0) uniform sampler uLinearSampler;
layout(set = 6, binding = 0) uniform texture2D uShadowMapTexture;
layout(set = 7, binding = 0) uniform sampler uShadowMapSampler;
layout(set = 0, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
mat4 gNormalMatrix;
};
const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData {
vec4 colorAndIntensity;
vec4 positionAndRange;
vec4 directionAndType;
vec4 spotAnglesAndFlags;
};
layout(set = 1, binding = 0, std140) uniform LightingConstants {
vec4 gMainLightDirectionAndIntensity;
vec4 gMainLightColorAndFlags;
vec4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
layout(set = 2, binding = 0, std140) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
layout(set = 3, binding = 0, std140) uniform ShadowReceiverConstants {
mat4 gWorldToShadowMatrix;
vec4 gShadowBiasAndTexelSize;
vec4 gShadowOptions;
};
layout(location = 0) in vec3 vNormalWS;
layout(location = 1) in vec2 vTexCoord;
layout(location = 2) in vec3 vPositionWS;
layout(location = 0) out vec4 fragColor;
float ComputeShadowAttenuation(vec3 positionWS) {
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0;
#else
if (gShadowOptions.x < 0.5) {
return 1.0;
}
vec4 shadowClip = gWorldToShadowMatrix * vec4(positionWS, 1.0);
if (shadowClip.w <= 0.0) {
return 1.0;
}
vec3 shadowNdc = shadowClip.xyz / shadowClip.w;
vec2 shadowUv = vec2(
shadowNdc.x * 0.5 + 0.5,
shadowNdc.y * -0.5 + 0.5);
if (shadowUv.x < 0.0 || shadowUv.x > 1.0 ||
shadowUv.y < 0.0 || shadowUv.y > 1.0 ||
shadowNdc.z < 0.0 || shadowNdc.z > 1.0) {
return 1.0;
}
float shadowDepth = texture(sampler2D(uShadowMapTexture, uShadowMapSampler), shadowUv).r;
float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0);
return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range) {
if (range <= 0.0) {
return 0.0;
}
float clampedRange = max(range, 0.0001);
float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0;
}
float distance = sqrt(max(distanceSq, 0.0));
float normalized = clamp(1.0 - distance / clampedRange, 0.0, 1.0);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, vec3 directionToLightWS) {
float cosOuter = light.spotAnglesAndFlags.x;
float cosInner = light.spotAnglesAndFlags.y;
vec3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return clamp((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4), 0.0, 1.0);
}
vec3 EvaluateAdditionalLight(AdditionalLightData light, vec3 normalWS, vec3 positionWS) {
float lightType = light.directionAndType.w;
vec3 lightColor = light.colorAndIntensity.rgb;
float lightIntensity = light.colorAndIntensity.w;
vec3 directionToLightWS = vec3(0.0);
float attenuation = 1.0;
if (lightType < 0.5) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
vec3 lightVectorWS = light.positionAndRange.xyz - positionWS;
float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6) {
return vec3(0.0);
}
directionToLightWS = normalize(lightVectorWS);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0) {
return vec3(0.0);
}
if (lightType > 1.5) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0) {
return vec3(0.0);
}
}
}
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
if (diffuse <= 0.0) {
return vec3(0.0);
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
void main() {
vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor;
#ifdef XC_ALPHA_TEST
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
int additionalLightCount = min(int(gLightingParams.x + 0.5), XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.w < 0.5 && additionalLightCount == 0) {
fragColor = baseColor;
return;
}
vec3 normalWS = normalize(vNormalWS);
vec3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.w >= 0.5) {
vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
float shadowAttenuation = diffuse > 0.0
? ComputeShadowAttenuation(vPositionWS)
: 1.0;
lighting +=
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, vPositionWS);
}
fragColor = vec4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -1,174 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_D3D12_PS
Texture2D gBaseColorTexture : register(t0);
SamplerState gLinearSampler : register(s0);
Texture2D gShadowMapTexture : register(t1);
SamplerState gShadowMapSampler : register(s1);
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
float4x4 gNormalMatrix;
};
static const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData {
float4 colorAndIntensity;
float4 positionAndRange;
float4 directionAndType;
float4 spotAnglesAndFlags;
};
cbuffer LightingConstants : register(b1) {
float4 gMainLightDirectionAndIntensity;
float4 gMainLightColorAndFlags;
float4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
cbuffer MaterialConstants : register(b2) {
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
cbuffer ShadowReceiverConstants : register(b3) {
float4x4 gWorldToShadowMatrix;
float4 gShadowBiasAndTexelSize;
float4 gShadowOptions;
};
struct PSInput {
float4 position : SV_POSITION;
float3 normalWS : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 positionWS : TEXCOORD2;
};
float ComputeShadowAttenuation(float3 positionWS) {
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0f;
#else
if (gShadowOptions.x < 0.5f) {
return 1.0f;
}
float4 shadowClip = mul(gWorldToShadowMatrix, float4(positionWS, 1.0f));
if (shadowClip.w <= 0.0f) {
return 1.0f;
}
float3 shadowNdc = shadowClip.xyz / shadowClip.w;
float2 shadowUv = float2(
shadowNdc.x * 0.5f + 0.5f,
shadowNdc.y * -0.5f + 0.5f);
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
shadowNdc.z < 0.0f || shadowNdc.z > 1.0f) {
return 1.0f;
}
const float shadowDepth = gShadowMapTexture.Sample(gShadowMapSampler, shadowUv).r;
const float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
const float shadowStrength = saturate(gShadowBiasAndTexelSize.w);
return receiverDepth <= shadowDepth ? 1.0f : (1.0f - shadowStrength);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range) {
if (range <= 0.0f) {
return 0.0f;
}
const float clampedRange = max(range, 0.0001f);
const float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0f;
}
const float distance = sqrt(max(distanceSq, 0.0f));
const float normalized = saturate(1.0f - distance / clampedRange);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, float3 directionToLightWS) {
const float cosOuter = light.spotAnglesAndFlags.x;
const float cosInner = light.spotAnglesAndFlags.y;
const float3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
const float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return saturate((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4f));
}
float3 EvaluateAdditionalLight(AdditionalLightData light, float3 normalWS, float3 positionWS) {
const float lightType = light.directionAndType.w;
const float3 lightColor = light.colorAndIntensity.rgb;
const float lightIntensity = light.colorAndIntensity.w;
float3 directionToLightWS = float3(0.0f, 0.0f, 0.0f);
float attenuation = 1.0f;
if (lightType < 0.5f) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
const float3 lightVectorWS = light.positionAndRange.xyz - positionWS;
const float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6f) {
return 0.0f.xxx;
}
directionToLightWS = lightVectorWS * rsqrt(distanceSq);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
if (lightType > 1.5f) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
}
}
const float diffuse = saturate(dot(normalWS, directionToLightWS));
if (diffuse <= 0.0f) {
return 0.0f.xxx;
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
float4 MainPS(PSInput input) : SV_TARGET {
float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor;
#ifdef XC_ALPHA_TEST
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
const int additionalLightCount = min((int)gLightingParams.x, XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.a < 0.5f && additionalLightCount == 0) {
return baseColor;
}
float3 normalWS = normalize(input.normalWS);
float3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.a >= 0.5f) {
float3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = saturate(dot(normalWS, directionToLightWS));
float shadowAttenuation = diffuse > 0.0f
? ComputeShadowAttenuation(input.positionWS)
: 1.0f;
lighting +=
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
[unroll]
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, input.positionWS);
}
return float4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -6,31 +6,240 @@ Shader "Builtin Forward Lit"
_Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
HLSLINCLUDE
cbuffer PerObjectConstants : register(b0)
{
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
float4x4 gNormalMatrix;
};
static const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData
{
float4 colorAndIntensity;
float4 positionAndRange;
float4 directionAndType;
float4 spotAnglesAndFlags;
};
cbuffer LightingConstants : register(b1)
{
float4 gMainLightDirectionAndIntensity;
float4 gMainLightColorAndFlags;
float4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
cbuffer MaterialConstants : register(b2)
{
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
cbuffer ShadowReceiverConstants : register(b3)
{
float4x4 gWorldToShadowMatrix;
float4 gShadowBiasAndTexelSize;
float4 gShadowOptions;
};
Texture2D BaseColorTexture : register(t0);
SamplerState LinearClampSampler : register(s0);
Texture2D ShadowMapTexture : register(t1);
SamplerState ShadowMapSampler : register(s1);
struct VSInput
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float3 normalWS : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 positionWS : TEXCOORD2;
};
PSInput MainVS(VSInput input)
{
PSInput output;
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
const float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.normalWS = mul((float3x3)gNormalMatrix, input.normal);
output.texcoord = input.texcoord;
output.positionWS = positionWS.xyz;
return output;
}
float ComputeShadowAttenuation(float3 positionWS)
{
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0f;
#else
if (gShadowOptions.x < 0.5f) {
return 1.0f;
}
const float4 shadowClip = mul(gWorldToShadowMatrix, float4(positionWS, 1.0f));
if (shadowClip.w <= 0.0f) {
return 1.0f;
}
const float3 shadowNdc = shadowClip.xyz / shadowClip.w;
#if UNITY_UV_STARTS_AT_TOP
const float shadowUvY = shadowNdc.y * -0.5f + 0.5f;
#else
const float shadowUvY = shadowNdc.y * 0.5f + 0.5f;
#endif
const float2 shadowUv = float2(shadowNdc.x * 0.5f + 0.5f, shadowUvY);
#if UNITY_NEAR_CLIP_VALUE < 0
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
shadowNdc.z < -1.0f || shadowNdc.z > 1.0f) {
return 1.0f;
}
const float receiverDepth = shadowNdc.z * 0.5f + 0.5f - gShadowBiasAndTexelSize.x;
#else
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
shadowNdc.z < 0.0f || shadowNdc.z > 1.0f) {
return 1.0f;
}
const float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
#endif
const float shadowDepth = ShadowMapTexture.Sample(ShadowMapSampler, shadowUv).r;
const float shadowStrength = saturate(gShadowBiasAndTexelSize.w);
return receiverDepth <= shadowDepth ? 1.0f : (1.0f - shadowStrength);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range)
{
if (range <= 0.0f) {
return 0.0f;
}
const float clampedRange = max(range, 0.0001f);
const float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0f;
}
const float distance = sqrt(max(distanceSq, 0.0f));
const float normalized = saturate(1.0f - distance / clampedRange);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, float3 directionToLightWS)
{
const float cosOuter = light.spotAnglesAndFlags.x;
const float cosInner = light.spotAnglesAndFlags.y;
const float3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
const float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return saturate((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4f));
}
float3 EvaluateAdditionalLight(AdditionalLightData light, float3 normalWS, float3 positionWS)
{
const float lightType = light.directionAndType.w;
const float3 lightColor = light.colorAndIntensity.rgb;
const float lightIntensity = light.colorAndIntensity.w;
float3 directionToLightWS = float3(0.0f, 0.0f, 0.0f);
float attenuation = 1.0f;
if (lightType < 0.5f) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
const float3 lightVectorWS = light.positionAndRange.xyz - positionWS;
const float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6f) {
return 0.0f.xxx;
}
directionToLightWS = lightVectorWS * rsqrt(distanceSq);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
if (lightType > 1.5f) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
}
}
const float diffuse = saturate(dot(normalWS, directionToLightWS));
if (diffuse <= 0.0f) {
return 0.0f.xxx;
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
float4 MainPS(PSInput input) : SV_TARGET
{
float4 baseColor = BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor;
#ifdef XC_ALPHA_TEST
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
const int additionalLightCount = min((int)gLightingParams.x, XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.a < 0.5f && additionalLightCount == 0) {
return baseColor;
}
const float3 normalWS = normalize(input.normalWS);
float3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.a >= 0.5f) {
const float3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
const float diffuse = saturate(dot(normalWS, directionToLightWS));
const float shadowAttenuation =
diffuse > 0.0f ? ComputeShadowAttenuation(input.positionWS) : 1.0f;
lighting +=
gMainLightColorAndFlags.rgb *
(diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
[unroll]
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, input.positionWS);
}
return float4(baseColor.rgb * lighting, baseColor.a);
}
ENDHLSL
SubShader
{
Cull Back
ZWrite On
ZTest LEqual
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "ForwardBase" }
Resources
{
PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
LightingConstants (ConstantBuffer, 1, 0) [Semantic(Lighting)]
MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)]
ShadowReceiverConstants (ConstantBuffer, 3, 0) [Semantic(ShadowReceiver)]
BaseColorTexture (Texture2D, 4, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 5, 0) [Semantic(LinearClampSampler)]
ShadowMapTexture (Texture2D, 6, 0) [Semantic(ShadowMapTexture)]
ShadowMapSampler (Sampler, 7, 0) [Semantic(ShadowMapSampler)]
}
HLSLPROGRAM
#pragma target 4.5
#pragma vertex MainVS
#pragma fragment MainPS
#pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS
#pragma shader_feature_local _ XC_ALPHA_TEST
#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
}
}

View File

@@ -1,25 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_OPENGL_VS
#version 430
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoord;
layout(std140, binding = 0) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
mat4 gNormalMatrix;
};
out vec3 vNormalWS;
out vec2 vTexCoord;
out vec3 vPositionWS;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vNormalWS = mat3(gNormalMatrix) * aNormal;
vTexCoord = aTexCoord;
vPositionWS = positionWS.xyz;
}

View File

@@ -1,25 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_VULKAN_VS
#version 450
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoord;
layout(set = 0, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
mat4 gNormalMatrix;
};
layout(location = 0) out vec3 vNormalWS;
layout(location = 1) out vec2 vTexCoord;
layout(location = 2) out vec3 vPositionWS;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vNormalWS = mat3(gNormalMatrix) * aNormal;
vTexCoord = aTexCoord;
vPositionWS = positionWS.xyz;
}

View File

@@ -1,31 +0,0 @@
// XC_BUILTIN_FORWARD_LIT_D3D12_VS
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
float4x4 gNormalMatrix;
};
struct VSInput {
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput {
float4 position : SV_POSITION;
float3 normalWS : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 positionWS : TEXCOORD2;
};
PSInput MainVS(VSInput input) {
PSInput output;
float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.normalWS = mul((float3x3)gNormalMatrix, input.normal);
output.texcoord = input.texcoord;
output.positionWS = positionWS.xyz;
return output;
}

View File

@@ -1,21 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_OPENGL_PS
#version 430
layout(binding = 0) uniform sampler2D uBaseColorTexture;
layout(std140, binding = 1) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
in vec2 vTexCoord;
void main() {
#ifdef XC_ALPHA_TEST
vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor;
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
gl_FragDepth = gl_FragCoord.z;
}

View File

@@ -1,22 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_VULKAN_PS
#version 450
layout(set = 2, binding = 0) uniform texture2D uBaseColorTexture;
layout(set = 3, binding = 0) uniform sampler uLinearSampler;
layout(set = 1, binding = 0, std140) uniform MaterialConstants {
vec4 gBaseColorFactor;
vec4 gAlphaCutoffParams;
};
layout(location = 0) in vec2 vTexCoord;
void main() {
#ifdef XC_ALPHA_TEST
vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor;
if (baseColor.a < gAlphaCutoffParams.x) {
discard;
}
#endif
gl_FragDepth = gl_FragCoord.z;
}

View File

@@ -1,21 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_D3D12_PS
Texture2D gBaseColorTexture : register(t0);
SamplerState gLinearSampler : register(s0);
cbuffer MaterialConstants : register(b1) {
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
struct PSInput {
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
float MainPS(PSInput input) : SV_Depth {
#ifdef XC_ALPHA_TEST
float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
return input.position.z;
}

View File

@@ -6,26 +6,67 @@ Shader "Builtin Shadow Caster"
_Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
HLSLINCLUDE
cbuffer PerObjectConstants : register(b0)
{
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
cbuffer MaterialConstants : register(b1)
{
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
Texture2D BaseColorTexture : register(t0);
SamplerState LinearClampSampler : register(s0);
struct VSInput
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input)
{
PSInput output;
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
const float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}
float MainPS(PSInput input) : SV_Depth
{
#ifdef XC_ALPHA_TEST
const float4 baseColor =
BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
return input.position.z;
}
ENDHLSL
SubShader
{
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
Resources
{
PerObjectConstants (ConstantBuffer, 0, 0) [Semantic(PerObject)]
MaterialConstants (ConstantBuffer, 1, 0) [Semantic(Material)]
BaseColorTexture (Texture2D, 2, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 3, 0) [Semantic(LinearClampSampler)]
}
HLSLPROGRAM
#pragma target 4.5
#pragma vertex MainVS
#pragma fragment MainPS
#pragma shader_feature_local _ XC_ALPHA_TEST
#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
}
}

View File

@@ -1,19 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_OPENGL_VS
#version 430
layout(location = 0) in vec3 aPosition;
layout(location = 2) in vec2 aTexCoord;
layout(std140, binding = 0) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,19 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_VULKAN_VS
#version 450
layout(location = 0) in vec3 aPosition;
layout(location = 2) in vec2 aTexCoord;
layout(set = 0, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
layout(location = 0) out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,25 +0,0 @@
// XC_BUILTIN_SHADOW_CASTER_D3D12_VS
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
struct VSInput {
float3 position : POSITION;
float2 texcoord : TEXCOORD0;
};
struct PSInput {
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input) {
PSInput output;
float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include <d3d12.h>
#include <d3dcompiler.h>
#include <dxgi1_4.h>
#include <wrl/client.h>
#include <string>
@@ -22,6 +23,12 @@ public:
bool CompileFromFile(const wchar_t* filePath, const char* entryPoint, const char* target) override;
bool Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target) override;
bool Compile(
const void* sourceData,
size_t sourceSize,
const D3D_SHADER_MACRO* macros,
const char* entryPoint,
const char* target);
void Shutdown() override;
const D3D12_SHADER_BYTECODE GetD3D12Bytecode() const;
@@ -48,4 +55,4 @@ private:
};
} // namespace RHI
} // namespace XCEngine
} // namespace XCEngine

View File

@@ -21,6 +21,7 @@
#include <stdio.h>
#include <memory>
#include <string>
#include <vector>
#ifdef _DEBUG
#include <dxgidebug.h>
@@ -51,7 +52,36 @@ bool CompileD3D12Shader(const ShaderCompileDesc& desc, D3D12Shader& shader) {
const char* profilePtr = profile.empty() ? nullptr : profile.c_str();
if (!desc.source.empty()) {
return shader.Compile(desc.source.data(), desc.source.size(), entryPointPtr, profilePtr);
std::vector<std::string> macroNames;
std::vector<std::string> macroDefinitions;
std::vector<D3D_SHADER_MACRO> macroTable;
if (!desc.macros.empty()) {
macroNames.reserve(desc.macros.size());
macroDefinitions.reserve(desc.macros.size());
macroTable.reserve(desc.macros.size() + 1u);
for (const ShaderCompileMacro& macro : desc.macros) {
macroNames.push_back(NarrowAscii(macro.name));
macroDefinitions.push_back(NarrowAscii(macro.definition));
}
for (size_t macroIndex = 0; macroIndex < desc.macros.size(); ++macroIndex) {
D3D_SHADER_MACRO d3dMacro = {};
d3dMacro.Name = macroNames[macroIndex].c_str();
d3dMacro.Definition = macroDefinitions[macroIndex].empty()
? "1"
: macroDefinitions[macroIndex].c_str();
macroTable.push_back(d3dMacro);
}
macroTable.push_back({ nullptr, nullptr });
}
const D3D_SHADER_MACRO* macroPtr = macroTable.empty() ? nullptr : macroTable.data();
return shader.Compile(
desc.source.data(),
desc.source.size(),
macroPtr,
entryPointPtr,
profilePtr);
}
if (!desc.fileName.empty()) {

View File

@@ -44,8 +44,26 @@ bool D3D12Shader::CompileFromFile(const wchar_t* filePath, const char* entryPoin
}
bool D3D12Shader::Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target) {
HRESULT hResult = D3DCompile(sourceData, sourceSize, nullptr, nullptr, nullptr, entryPoint, target,
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION, 0, &m_bytecode, &m_error);
return Compile(sourceData, sourceSize, nullptr, entryPoint, target);
}
bool D3D12Shader::Compile(const void* sourceData,
size_t sourceSize,
const D3D_SHADER_MACRO* macros,
const char* entryPoint,
const char* target) {
HRESULT hResult = D3DCompile(
sourceData,
sourceSize,
nullptr,
macros,
nullptr,
entryPoint,
target,
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
0,
&m_bytecode,
&m_error);
if (FAILED(hResult)) {
if (m_error) {

View File

@@ -29,7 +29,10 @@
#include <cctype>
#include <filesystem>
#include <fstream>
#include <regex>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc);
@@ -166,6 +169,167 @@ bool LoadShaderSourceText(const ShaderCompileDesc& desc, std::string& outSourceT
return file.read(outSourceText.data(), size).good();
}
bool IsHlslShaderCompileDesc(const ShaderCompileDesc& desc) {
if (desc.sourceLanguage == ShaderLanguage::HLSL) {
return true;
}
if (desc.fileName.empty()) {
return false;
}
std::string extension = std::filesystem::path(desc.fileName).extension().string();
std::transform(extension.begin(), extension.end(), extension.begin(), [](unsigned char ch) {
return static_cast<char>(std::tolower(ch));
});
return extension == ".hlsl";
}
void CollectHlslTextureRegisterBindings(
const std::string& sourceText,
std::unordered_map<std::string, GLint>& outTextureUnits,
std::unordered_set<std::string>& outSamplerNames) {
static const std::regex kTexturePattern(
R"(((?:Texture2D|TextureCube)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*register\s*\(\s*t([0-9]+)))",
std::regex::ECMAScript);
static const std::regex kSamplerPattern(
R"(((?:SamplerState|SamplerComparisonState)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*register\s*\(\s*s([0-9]+)))",
std::regex::ECMAScript);
for (std::sregex_iterator it(sourceText.begin(), sourceText.end(), kTexturePattern), end; it != end; ++it) {
const std::smatch& match = *it;
if (match.size() < 4) {
continue;
}
try {
outTextureUnits[match[2].str()] = static_cast<GLint>(std::stoi(match[3].str()));
} catch (...) {
}
}
for (std::sregex_iterator it(sourceText.begin(), sourceText.end(), kSamplerPattern), end; it != end; ++it) {
const std::smatch& match = *it;
if (match.size() < 4) {
continue;
}
outSamplerNames.insert(match[2].str());
}
}
bool TryResolveCombinedSamplerTextureUnit(
const std::string& uniformName,
const std::unordered_map<std::string, GLint>& textureUnits,
const std::unordered_set<std::string>& samplerNames,
GLint& outTextureUnit) {
static const std::string kCombinedPrefix = "SPIRV_Cross_Combined";
if (uniformName.rfind(kCombinedPrefix, 0) != 0) {
return false;
}
std::string combinedName = uniformName.substr(kCombinedPrefix.size());
const size_t arraySuffixPos = combinedName.find('[');
if (arraySuffixPos != std::string::npos) {
combinedName = combinedName.substr(0, arraySuffixPos);
}
std::vector<std::pair<std::string, GLint>> candidates(textureUnits.begin(), textureUnits.end());
std::sort(
candidates.begin(),
candidates.end(),
[](const auto& left, const auto& right) {
return left.first.size() > right.first.size();
});
for (const auto& [textureName, textureUnit] : candidates) {
if (combinedName.rfind(textureName, 0) != 0) {
continue;
}
const std::string samplerName = combinedName.substr(textureName.size());
if (!samplerName.empty() &&
(samplerNames.empty() || samplerNames.find(samplerName) != samplerNames.end())) {
outTextureUnit = textureUnit;
return true;
}
}
return false;
}
void ConfigureOpenGLCombinedSamplerUniforms(
GLuint program,
const ShaderCompileDesc* shaderDescs,
size_t shaderDescCount) {
if (program == 0 || shaderDescs == nullptr || shaderDescCount == 0) {
return;
}
std::unordered_map<std::string, GLint> textureUnits;
std::unordered_set<std::string> samplerNames;
for (size_t shaderIndex = 0; shaderIndex < shaderDescCount; ++shaderIndex) {
const ShaderCompileDesc& shaderDesc = shaderDescs[shaderIndex];
if (!IsHlslShaderCompileDesc(shaderDesc)) {
continue;
}
std::string sourceText;
if (!LoadShaderSourceText(shaderDesc, sourceText) || sourceText.empty()) {
continue;
}
CollectHlslTextureRegisterBindings(sourceText, textureUnits, samplerNames);
}
if (textureUnits.empty()) {
return;
}
GLint activeUniformCount = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniformCount);
if (activeUniformCount <= 0) {
return;
}
GLint previousProgram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgram);
glUseProgram(program);
for (GLint uniformIndex = 0; uniformIndex < activeUniformCount; ++uniformIndex) {
GLsizei nameLength = 0;
GLint arraySize = 0;
GLenum uniformType = 0;
char nameBuffer[256] = {};
glGetActiveUniform(
program,
static_cast<GLuint>(uniformIndex),
static_cast<GLsizei>(std::size(nameBuffer)),
&nameLength,
&arraySize,
&uniformType,
nameBuffer);
if (nameLength <= 0) {
continue;
}
(void)arraySize;
(void)uniformType;
GLint textureUnit = -1;
if (!TryResolveCombinedSamplerTextureUnit(nameBuffer, textureUnits, samplerNames, textureUnit)) {
continue;
}
const GLint location = glGetUniformLocation(program, nameBuffer);
if (location >= 0) {
glUniform1i(location, textureUnit);
}
}
glUseProgram(static_cast<GLuint>(previousProgram));
}
bool TryResolveShaderTypeFromTarget(const std::wstring& profile, ShaderType& type) {
const std::string asciiProfile = NarrowAscii(profile);
if (asciiProfile.find("vs_") != std::string::npos || asciiProfile == "vs") {
@@ -536,6 +700,8 @@ bool BuildOpenGLProgram(const ShaderCompileDesc* shaderDescs,
return false;
}
ConfigureOpenGLCombinedSamplerUniforms(program, shaderDescs, shaderDescCount);
outProgram = program;
return true;
}

View File

@@ -1,5 +1,7 @@
#include <gtest/gtest.h>
#include "Rendering/Detail/ShaderVariantUtils.h"
#include <XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h>
#include <XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h>
#include <XCEngine/Rendering/Passes/BuiltinObjectIdPass.h>
@@ -10,6 +12,10 @@
#include <XCEngine/Resources/Shader/Shader.h>
#include <XCEngine/Resources/Shader/ShaderLoader.h>
#include <XCEngine/RHI/RHIEnums.h>
#include <XCEngine/RHI/ShaderCompiler/SpirvShaderCompiler.h>
#include <fstream>
#include <iostream>
using namespace XCEngine::Rendering::Pipelines;
using namespace XCEngine::Rendering::Passes;
@@ -53,7 +59,7 @@ TEST(BuiltinForwardPipeline_Test, SplitsSceneItemsIntoOpaqueAndTransparentQueueR
EXPECT_TRUE(IsTransparentRenderQueue(static_cast<int>(MaterialRenderQueue::Overlay)));
}
TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderDeclaresExplicitForwardResourceContract) {
TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderUsesUnityStyleSingleSourceSurfaceContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());
ASSERT_TRUE(result);
@@ -69,52 +75,16 @@ TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderDeclaresExplicitForwardRes
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 8u);
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);
EXPECT_EQ(pass->resources[1].semantic, "Lighting");
EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[1].set, 1u);
EXPECT_EQ(pass->resources[1].binding, 0u);
EXPECT_EQ(pass->resources[2].semantic, "Material");
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[2].set, 2u);
EXPECT_EQ(pass->resources[2].binding, 0u);
EXPECT_EQ(pass->resources[3].semantic, "ShadowReceiver");
EXPECT_EQ(pass->resources[3].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[3].set, 3u);
EXPECT_EQ(pass->resources[3].binding, 0u);
EXPECT_EQ(pass->resources[4].semantic, "BaseColorTexture");
EXPECT_EQ(pass->resources[4].type, ShaderResourceType::Texture2D);
EXPECT_EQ(pass->resources[4].set, 4u);
EXPECT_EQ(pass->resources[4].binding, 0u);
EXPECT_EQ(pass->resources[5].semantic, "LinearClampSampler");
EXPECT_EQ(pass->resources[5].type, ShaderResourceType::Sampler);
EXPECT_EQ(pass->resources[5].set, 5u);
EXPECT_EQ(pass->resources[5].binding, 0u);
EXPECT_EQ(pass->resources[6].semantic, "ShadowMapTexture");
EXPECT_EQ(pass->resources[6].type, ShaderResourceType::Texture2D);
EXPECT_EQ(pass->resources[6].set, 6u);
EXPECT_EQ(pass->resources[6].binding, 0u);
EXPECT_EQ(pass->resources[7].semantic, "ShadowMapSampler");
EXPECT_EQ(pass->resources[7].type, ShaderResourceType::Sampler);
EXPECT_EQ(pass->resources[7].set, 7u);
EXPECT_EQ(pass->resources[7].binding, 0u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
delete shader;
}
TEST(BuiltinForwardPipeline_Test, BuiltinUnlitShaderDeclaresExplicitSurfaceResourceContract) {
TEST(BuiltinForwardPipeline_Test, BuiltinUnlitShaderUsesUnityStyleSingleSourceSurfaceContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinUnlitShaderPath());
ASSERT_TRUE(result);
@@ -125,27 +95,362 @@ TEST(BuiltinForwardPipeline_Test, BuiltinUnlitShaderDeclaresExplicitSurfaceResou
const ShaderPass* pass = shader->FindPass("Unlit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 4u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
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);
delete shader;
}
EXPECT_EQ(pass->resources[1].semantic, "Material");
EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[1].set, 1u);
EXPECT_EQ(pass->resources[1].binding, 0u);
TEST(BuiltinForwardPipeline_Test, BuiltinUnlitShaderBuildsVulkanRuntimeSourceWithResolvedBindings) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinUnlitShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
EXPECT_EQ(pass->resources[2].semantic, "BaseColorTexture");
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D);
EXPECT_EQ(pass->resources[2].set, 2u);
EXPECT_EQ(pass->resources[2].binding, 0u);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
EXPECT_EQ(pass->resources[3].semantic, "LinearClampSampler");
EXPECT_EQ(pass->resources[3].type, ShaderResourceType::Sampler);
EXPECT_EQ(pass->resources[3].set, 3u);
EXPECT_EQ(pass->resources[3].binding, 0u);
const ShaderPass* pass = shader->FindPass("Unlit");
ASSERT_NE(pass, nullptr);
const ShaderStageVariant* fragmentVariant =
shader->FindVariant(
"Unlit",
XCEngine::Resources::ShaderType::Fragment,
XCEngine::Resources::ShaderBackend::Vulkan);
ASSERT_NE(fragmentVariant, nullptr);
const std::string runtimeSource =
::XCEngine::Rendering::Detail::BuildRuntimeShaderSource(
*pass,
XCEngine::Resources::ShaderBackend::Vulkan,
*fragmentVariant);
EXPECT_NE(
runtimeSource.find("cbuffer MaterialConstants : register(b0, space1)"),
std::string::npos);
EXPECT_NE(runtimeSource.find("BaseColorTexture"), std::string::npos);
EXPECT_NE(runtimeSource.find("space2"), std::string::npos);
EXPECT_NE(runtimeSource.find("LinearClampSampler"), std::string::npos);
EXPECT_NE(runtimeSource.find("space3"), std::string::npos);
delete shader;
}
TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleUnlitBindingsToDescriptorSpaces) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinUnlitShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("Unlit");
ASSERT_NE(pass, nullptr);
const ShaderStageVariant* d3d12Fragment = shader->FindVariant(
"Unlit",
XCEngine::Resources::ShaderType::Fragment,
XCEngine::Resources::ShaderBackend::D3D12);
ASSERT_NE(d3d12Fragment, nullptr);
ShaderCompileDesc d3d12CompileDesc = {};
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
*pass,
XCEngine::Resources::ShaderBackend::D3D12,
*d3d12Fragment,
d3d12CompileDesc);
const std::string d3d12Source(
reinterpret_cast<const char*>(d3d12CompileDesc.source.data()),
d3d12CompileDesc.source.size());
EXPECT_NE(d3d12Source.find("BaseColorTexture"), std::string::npos);
EXPECT_NE(d3d12Source.find("LinearClampSampler"), std::string::npos);
EXPECT_EQ(d3d12Source.find("space2"), std::string::npos);
EXPECT_EQ(d3d12Source.find("space3"), std::string::npos);
const ShaderStageVariant* vulkanFragment = shader->FindVariant(
"Unlit",
XCEngine::Resources::ShaderType::Fragment,
XCEngine::Resources::ShaderBackend::Vulkan);
ASSERT_NE(vulkanFragment, nullptr);
ShaderCompileDesc vulkanCompileDesc = {};
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
*pass,
XCEngine::Resources::ShaderBackend::Vulkan,
*vulkanFragment,
vulkanCompileDesc);
const std::string vulkanSource(
reinterpret_cast<const char*>(vulkanCompileDesc.source.data()),
vulkanCompileDesc.source.size());
EXPECT_NE(vulkanSource.find("cbuffer MaterialConstants : register(b0, space1)"), std::string::npos);
EXPECT_NE(vulkanSource.find("BaseColorTexture"), std::string::npos);
EXPECT_NE(vulkanSource.find("space2"), std::string::npos);
EXPECT_NE(vulkanSource.find("LinearClampSampler"), std::string::npos);
EXPECT_NE(vulkanSource.find("space3"), std::string::npos);
delete shader;
}
TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleForwardBindingsToDescriptorSpaces) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ShaderKeywordSet shadowKeywords = {};
shadowKeywords.enabledKeywords.PushBack("XC_MAIN_LIGHT_SHADOWS");
const ShaderStageVariant* d3d12Fragment = shader->FindVariant(
"ForwardLit",
XCEngine::Resources::ShaderType::Fragment,
XCEngine::Resources::ShaderBackend::D3D12,
shadowKeywords);
ASSERT_NE(d3d12Fragment, nullptr);
ShaderCompileDesc d3d12CompileDesc = {};
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
*pass,
XCEngine::Resources::ShaderBackend::D3D12,
*d3d12Fragment,
d3d12CompileDesc);
const std::string d3d12Source(
reinterpret_cast<const char*>(d3d12CompileDesc.source.data()),
d3d12CompileDesc.source.size());
EXPECT_NE(d3d12Source.find("cbuffer LightingConstants : register(b1)"), std::string::npos);
EXPECT_NE(d3d12Source.find("Texture2D ShadowMapTexture : register(t1)"), std::string::npos);
EXPECT_EQ(d3d12Source.find("space1"), std::string::npos);
EXPECT_EQ(d3d12Source.find("space6"), std::string::npos);
const ShaderStageVariant* vulkanFragment = shader->FindVariant(
"ForwardLit",
XCEngine::Resources::ShaderType::Fragment,
XCEngine::Resources::ShaderBackend::Vulkan,
shadowKeywords);
ASSERT_NE(vulkanFragment, nullptr);
ShaderCompileDesc vulkanCompileDesc = {};
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
*pass,
XCEngine::Resources::ShaderBackend::Vulkan,
*vulkanFragment,
vulkanCompileDesc);
const std::string vulkanSource(
reinterpret_cast<const char*>(vulkanCompileDesc.source.data()),
vulkanCompileDesc.source.size());
EXPECT_NE(vulkanSource.find("cbuffer PerObjectConstants : register(b0, space0)"), std::string::npos);
EXPECT_NE(vulkanSource.find("cbuffer LightingConstants : register(b0, space1)"), std::string::npos);
EXPECT_NE(vulkanSource.find("cbuffer MaterialConstants : register(b0, space2)"), std::string::npos);
EXPECT_NE(vulkanSource.find("cbuffer ShadowReceiverConstants : register(b0, space3)"), std::string::npos);
EXPECT_NE(vulkanSource.find("Texture2D BaseColorTexture : register(t0, space4)"), std::string::npos);
EXPECT_NE(vulkanSource.find("SamplerState LinearClampSampler : register(s0, space5)"), std::string::npos);
EXPECT_NE(vulkanSource.find("Texture2D ShadowMapTexture : register(t0, space6)"), std::string::npos);
EXPECT_NE(vulkanSource.find("SamplerState ShadowMapSampler : register(s0, space7)"), std::string::npos);
delete shader;
}
TEST(BuiltinForwardPipeline_Test, OpenGLRuntimeTranspilesForwardShadowVariantToLegacyClipConventions) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ShaderKeywordSet shadowKeywords = {};
shadowKeywords.enabledKeywords.PushBack("XC_MAIN_LIGHT_SHADOWS");
const ShaderStageVariant* openGLFragment = shader->FindVariant(
"ForwardLit",
XCEngine::Resources::ShaderType::Fragment,
XCEngine::Resources::ShaderBackend::OpenGL,
shadowKeywords);
ASSERT_NE(openGLFragment, nullptr);
ShaderCompileDesc compileDesc = {};
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
*pass,
XCEngine::Resources::ShaderBackend::OpenGL,
*openGLFragment,
compileDesc);
const std::string runtimeSource(
reinterpret_cast<const char*>(compileDesc.source.data()),
compileDesc.source.size());
EXPECT_NE(runtimeSource.find("const float shadowUvY = shadowNdc.y * 0.5f + 0.5f;"), std::string::npos);
EXPECT_NE(
runtimeSource.find("shadowNdc.z < -1.0f || shadowNdc.z > 1.0f"),
std::string::npos);
EXPECT_NE(
runtimeSource.find(
"const float receiverDepth = shadowNdc.z * 0.5f + 0.5f - gShadowBiasAndTexelSize.x;"),
std::string::npos);
XCEngine::RHI::CompiledSpirvShader spirvShader = {};
std::string errorMessage;
ASSERT_TRUE(
XCEngine::RHI::CompileSpirvShader(
compileDesc,
XCEngine::RHI::SpirvTargetEnvironment::Vulkan,
spirvShader,
&errorMessage))
<< errorMessage;
std::string glslSource;
ASSERT_TRUE(XCEngine::RHI::TranspileSpirvToOpenGLGLSL(spirvShader, glslSource, &errorMessage))
<< errorMessage;
EXPECT_NE(
glslSource.find("uniform sampler2D SPIRV_Cross_CombinedBaseColorTextureLinearClampSampler;"),
std::string::npos);
EXPECT_NE(
glslSource.find("uniform sampler2D SPIRV_Cross_CombinedShadowMapTextureShadowMapSampler;"),
std::string::npos);
EXPECT_NE(
glslSource.find("spvWorkaroundRowMajor(ShadowReceiverConstants.gWorldToShadowMatrix)"),
std::string::npos);
EXPECT_NE(glslSource.find("texture(SPIRV_Cross_CombinedShadowMapTextureShadowMapSampler"), std::string::npos);
delete shader;
}
TEST(BuiltinDepthStylePass_Test, OpenGLRuntimeTranspilesDepthOnlyAlphaVariantWithTexcoordAtLocation2) {
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);
const ShaderPass* pass = shader->FindPass("DepthOnly");
ASSERT_NE(pass, nullptr);
ShaderKeywordSet alphaKeywords = {};
alphaKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
const ShaderStageVariant* openGLVertex = shader->FindVariant(
"DepthOnly",
XCEngine::Resources::ShaderType::Vertex,
ShaderBackend::OpenGL,
alphaKeywords);
ASSERT_NE(openGLVertex, nullptr);
const ShaderStageVariant* openGLFragment = shader->FindVariant(
"DepthOnly",
XCEngine::Resources::ShaderType::Fragment,
ShaderBackend::OpenGL,
alphaKeywords);
ASSERT_NE(openGLFragment, nullptr);
auto transpileStage = [](const ShaderPass& targetPass,
const ShaderStageVariant& variant,
std::string& glslSource) {
ShaderCompileDesc compileDesc = {};
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
targetPass,
XCEngine::Resources::ShaderBackend::OpenGL,
variant,
compileDesc);
XCEngine::RHI::CompiledSpirvShader spirvShader = {};
std::string errorMessage;
EXPECT_TRUE(
XCEngine::RHI::CompileSpirvShader(
compileDesc,
XCEngine::RHI::SpirvTargetEnvironment::Vulkan,
spirvShader,
&errorMessage))
<< errorMessage;
EXPECT_TRUE(XCEngine::RHI::TranspileSpirvToOpenGLGLSL(spirvShader, glslSource, &errorMessage))
<< errorMessage;
};
std::string vertexGlsl;
transpileStage(*pass, *openGLVertex, vertexGlsl);
EXPECT_NE(vertexGlsl.find("layout(location = 2) in vec2"), std::string::npos);
std::string fragmentGlsl;
transpileStage(*pass, *openGLFragment, fragmentGlsl);
EXPECT_NE(fragmentGlsl.find("discard;"), std::string::npos);
EXPECT_NE(fragmentGlsl.find("gl_FragDepth"), std::string::npos);
delete shader;
}
TEST(BuiltinDepthStylePass_Test, OpenGLRuntimeTranspilesShadowCasterAlphaVariantWithTexcoordAtLocation2) {
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);
const ShaderPass* pass = shader->FindPass("ShadowCaster");
ASSERT_NE(pass, nullptr);
ShaderKeywordSet alphaKeywords = {};
alphaKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
const ShaderStageVariant* openGLVertex = shader->FindVariant(
"ShadowCaster",
XCEngine::Resources::ShaderType::Vertex,
ShaderBackend::OpenGL,
alphaKeywords);
ASSERT_NE(openGLVertex, nullptr);
const ShaderStageVariant* openGLFragment = shader->FindVariant(
"ShadowCaster",
XCEngine::Resources::ShaderType::Fragment,
ShaderBackend::OpenGL,
alphaKeywords);
ASSERT_NE(openGLFragment, nullptr);
auto transpileStage = [](const ShaderPass& targetPass,
const ShaderStageVariant& variant,
std::string& glslSource) {
ShaderCompileDesc compileDesc = {};
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
targetPass,
XCEngine::Resources::ShaderBackend::OpenGL,
variant,
compileDesc);
XCEngine::RHI::CompiledSpirvShader spirvShader = {};
std::string errorMessage;
EXPECT_TRUE(
XCEngine::RHI::CompileSpirvShader(
compileDesc,
XCEngine::RHI::SpirvTargetEnvironment::Vulkan,
spirvShader,
&errorMessage))
<< errorMessage;
EXPECT_TRUE(XCEngine::RHI::TranspileSpirvToOpenGLGLSL(spirvShader, glslSource, &errorMessage))
<< errorMessage;
};
std::string vertexGlsl;
transpileStage(*pass, *openGLVertex, vertexGlsl);
EXPECT_NE(vertexGlsl.find("layout(location = 2) in vec2"), std::string::npos);
std::string fragmentGlsl;
transpileStage(*pass, *openGLFragment, fragmentGlsl);
EXPECT_NE(fragmentGlsl.find("discard;"), std::string::npos);
EXPECT_NE(fragmentGlsl.find("gl_FragDepth"), std::string::npos);
delete shader;
}
@@ -263,7 +568,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinFinalColorShaderDeclaresExplicitFullscr
delete shader;
}
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplicitForwardResources) {
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoadedImplicitForwardShader) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());
ASSERT_TRUE(result);
@@ -277,7 +582,7 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplic
BuiltinPassResourceBindingPlan plan = {};
String error;
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
EXPECT_TRUE(plan.perObject.IsValid());
EXPECT_TRUE(plan.lighting.IsValid());
EXPECT_TRUE(plan.material.IsValid());
@@ -295,7 +600,7 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplic
delete shader;
}
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromImplicitForwardContract) {
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromBuiltinForwardShaderContract) {
ShaderPass pass = {};
pass.name = "ForwardLit";
@@ -319,7 +624,7 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromImplic
EXPECT_EQ(plan.descriptorSetCount, 8u);
}
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplicitUnlitResources) {
TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromImplicitUnlitContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinUnlitShaderPath());
ASSERT_TRUE(result);
@@ -333,7 +638,7 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplic
BuiltinPassResourceBindingPlan plan = {};
String error;
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
EXPECT_TRUE(plan.perObject.IsValid());
EXPECT_TRUE(plan.material.IsValid());
EXPECT_TRUE(plan.baseColorTexture.IsValid());
@@ -347,7 +652,7 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplic
delete shader;
}
TEST(BuiltinForwardPipeline_Test, UsesNormalizedExplicitSetIndicesForSurfaceResources) {
TEST(BuiltinForwardPipeline_Test, UsesNormalizedImplicitSetIndicesForForwardSurfaceResources) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());
ASSERT_TRUE(result);
@@ -358,11 +663,11 @@ TEST(BuiltinForwardPipeline_Test, UsesNormalizedExplicitSetIndicesForSurfaceReso
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 8u);
EXPECT_TRUE(pass->resources.Empty());
BuiltinPassResourceBindingPlan plan = {};
String error;
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
ASSERT_EQ(plan.bindings.Size(), 8u);
EXPECT_EQ(plan.perObject.set, 0u);
EXPECT_EQ(plan.lighting.set, 1u);
@@ -376,7 +681,7 @@ TEST(BuiltinForwardPipeline_Test, UsesNormalizedExplicitSetIndicesForSurfaceReso
delete shader;
}
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitForwardResources) {
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromImplicitForwardResources) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());
ASSERT_TRUE(result);
@@ -387,11 +692,11 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitForwardResources)
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 8u);
EXPECT_TRUE(pass->resources.Empty());
BuiltinPassResourceBindingPlan plan = {};
String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
@@ -436,7 +741,7 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitForwardResources)
delete shader;
}
TEST(BuiltinDepthStylePass_Test, BuiltinDepthOnlyShaderDeclaresExplicitPerObjectResourceContract) {
TEST(BuiltinDepthStylePass_Test, BuiltinDepthOnlyShaderUsesUnityStyleSingleSourceContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath());
ASSERT_TRUE(result);
@@ -447,29 +752,16 @@ TEST(BuiltinDepthStylePass_Test, BuiltinDepthOnlyShaderDeclaresExplicitPerObject
const ShaderPass* pass = shader->FindPass("DepthOnly");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 4u);
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);
EXPECT_EQ(pass->resources[1].semantic, "Material");
EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[1].set, 1u);
EXPECT_EQ(pass->resources[1].binding, 0u);
EXPECT_EQ(pass->resources[2].semantic, "BaseColorTexture");
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D);
EXPECT_EQ(pass->resources[2].set, 2u);
EXPECT_EQ(pass->resources[2].binding, 0u);
EXPECT_EQ(pass->resources[3].semantic, "LinearClampSampler");
EXPECT_EQ(pass->resources[3].type, ShaderResourceType::Sampler);
EXPECT_EQ(pass->resources[3].set, 3u);
EXPECT_EQ(pass->resources[3].binding, 0u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
delete shader;
}
TEST(BuiltinDepthStylePass_Test, BuiltinShadowCasterShaderDeclaresExplicitPerObjectResourceContract) {
TEST(BuiltinDepthStylePass_Test, BuiltinShadowCasterShaderUsesUnityStyleSingleSourceContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinShadowCasterShaderPath());
ASSERT_TRUE(result);
@@ -480,24 +772,11 @@ TEST(BuiltinDepthStylePass_Test, BuiltinShadowCasterShaderDeclaresExplicitPerObj
const ShaderPass* pass = shader->FindPass("ShadowCaster");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 4u);
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);
EXPECT_EQ(pass->resources[1].semantic, "Material");
EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[1].set, 1u);
EXPECT_EQ(pass->resources[1].binding, 0u);
EXPECT_EQ(pass->resources[2].semantic, "BaseColorTexture");
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D);
EXPECT_EQ(pass->resources[2].set, 2u);
EXPECT_EQ(pass->resources[2].binding, 0u);
EXPECT_EQ(pass->resources[3].semantic, "LinearClampSampler");
EXPECT_EQ(pass->resources[3].type, ShaderResourceType::Sampler);
EXPECT_EQ(pass->resources[3].set, 3u);
EXPECT_EQ(pass->resources[3].binding, 0u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
delete shader;
}
@@ -647,7 +926,7 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitObjectIdResources
delete shader;
}
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitDepthOnlyResources) {
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromBuiltinDepthOnlyShaderContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath());
ASSERT_TRUE(result);
@@ -658,11 +937,10 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitDepthOnlyResource
const ShaderPass* pass = shader->FindPass("DepthOnly");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 4u);
BuiltinPassResourceBindingPlan plan = {};
String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
EXPECT_TRUE(plan.perObject.IsValid());
EXPECT_TRUE(plan.material.IsValid());
EXPECT_TRUE(plan.baseColorTexture.IsValid());
@@ -703,7 +981,7 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitDepthOnlyResource
delete shader;
}
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitShadowCasterResources) {
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromBuiltinShadowCasterShaderContract) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinShadowCasterShaderPath());
ASSERT_TRUE(result);
@@ -714,11 +992,10 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitShadowCasterResou
const ShaderPass* pass = shader->FindPass("ShadowCaster");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 4u);
BuiltinPassResourceBindingPlan plan = {};
String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
EXPECT_TRUE(plan.perObject.IsValid());
EXPECT_TRUE(plan.material.IsValid());
EXPECT_TRUE(plan.baseColorTexture.IsValid());

View File

@@ -1547,7 +1547,7 @@ TEST(ShaderLoader, AssetDatabaseReimportsShaderWhenStageDependencyChanges) {
fs::remove_all(projectRoot);
}
TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsUnityStyleSingleSourceVariants) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());
ASSERT_TRUE(result);
@@ -1560,9 +1560,13 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(shader->GetProperties().Size(), 3u);
ASSERT_EQ(pass->variants.Size(), 24u);
ASSERT_EQ(pass->variants.Size(), 8u);
ASSERT_EQ(pass->tags.Size(), 1u);
ASSERT_EQ(pass->resources.Size(), 8u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
EXPECT_EQ(pass->tags[0].name, "LightMode");
EXPECT_EQ(pass->tags[0].value, "ForwardBase");
@@ -1581,45 +1585,11 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
EXPECT_EQ(cutoffProperty->type, ShaderPropertyType::Range);
EXPECT_EQ(cutoffProperty->semantic, "AlphaCutoff");
const ShaderResourceBindingDesc* perObjectBinding =
shader->FindPassResourceBinding("ForwardLit", "PerObjectConstants");
ASSERT_NE(perObjectBinding, nullptr);
EXPECT_EQ(perObjectBinding->type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(perObjectBinding->set, 0u);
EXPECT_EQ(perObjectBinding->binding, 0u);
EXPECT_EQ(perObjectBinding->semantic, "PerObject");
const ShaderResourceBindingDesc* lightingBinding =
shader->FindPassResourceBinding("ForwardLit", "LightingConstants");
ASSERT_NE(lightingBinding, nullptr);
EXPECT_EQ(lightingBinding->type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(lightingBinding->set, 1u);
EXPECT_EQ(lightingBinding->binding, 0u);
EXPECT_EQ(lightingBinding->semantic, "Lighting");
const ShaderResourceBindingDesc* shadowReceiverBinding =
shader->FindPassResourceBinding("ForwardLit", "ShadowReceiverConstants");
ASSERT_NE(shadowReceiverBinding, nullptr);
EXPECT_EQ(shadowReceiverBinding->type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(shadowReceiverBinding->set, 3u);
EXPECT_EQ(shadowReceiverBinding->binding, 0u);
EXPECT_EQ(shadowReceiverBinding->semantic, "ShadowReceiver");
const ShaderResourceBindingDesc* baseColorBinding =
shader->FindPassResourceBinding("ForwardLit", "BaseColorTexture");
ASSERT_NE(baseColorBinding, nullptr);
EXPECT_EQ(baseColorBinding->type, ShaderResourceType::Texture2D);
EXPECT_EQ(baseColorBinding->set, 4u);
EXPECT_EQ(baseColorBinding->binding, 0u);
EXPECT_EQ(baseColorBinding->semantic, "BaseColorTexture");
const ShaderResourceBindingDesc* shadowTextureBinding =
shader->FindPassResourceBinding("ForwardLit", "ShadowMapTexture");
ASSERT_NE(shadowTextureBinding, nullptr);
EXPECT_EQ(shadowTextureBinding->type, ShaderResourceType::Texture2D);
EXPECT_EQ(shadowTextureBinding->set, 6u);
EXPECT_EQ(shadowTextureBinding->binding, 0u);
EXPECT_EQ(shadowTextureBinding->semantic, "ShadowMapTexture");
EXPECT_EQ(shader->FindPassResourceBinding("ForwardLit", "PerObjectConstants"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("ForwardLit", "LightingConstants"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("ForwardLit", "ShadowReceiverConstants"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("ForwardLit", "BaseColorTexture"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("ForwardLit", "ShadowMapTexture"), nullptr);
ASSERT_EQ(pass->keywordDeclarations.Size(), 2u);
EXPECT_EQ(pass->keywordDeclarations[0].type, ShaderKeywordDeclarationType::MultiCompile);
ASSERT_EQ(pass->keywordDeclarations[0].options.Size(), 2u);
@@ -1631,7 +1601,7 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
EXPECT_EQ(pass->keywordDeclarations[1].options[1], "XC_ALPHA_TEST");
EXPECT_TRUE(shader->PassDeclaresKeyword("ForwardLit", "XC_MAIN_LIGHT_SHADOWS"));
EXPECT_TRUE(shader->PassDeclaresKeyword("ForwardLit", "XC_ALPHA_TEST"));
ASSERT_EQ(pass->variants.Size(), 24u);
ASSERT_EQ(pass->variants.Size(), 8u);
EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Fragment, ShaderBackend::D3D12), nullptr);
@@ -1645,13 +1615,21 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
ShaderType::Vertex,
ShaderBackend::D3D12);
ASSERT_NE(d3d12Vertex, nullptr);
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("XC_BUILTIN_FORWARD_LIT_D3D12_VS"), std::string::npos);
EXPECT_EQ(d3d12Vertex->backend, ShaderBackend::Generic);
EXPECT_EQ(d3d12Vertex->language, ShaderLanguage::HLSL);
EXPECT_EQ(d3d12Vertex->entryPoint, "MainVS");
EXPECT_EQ(d3d12Vertex->profile, "vs_5_0");
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("cbuffer LightingConstants"), std::string::npos);
const ShaderStageVariant* d3d12Fragment = shader->FindVariant(
"ForwardLit",
ShaderType::Fragment,
ShaderBackend::D3D12);
ASSERT_NE(d3d12Fragment, nullptr);
EXPECT_EQ(d3d12Fragment->backend, ShaderBackend::Generic);
EXPECT_EQ(d3d12Fragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(d3d12Fragment->entryPoint, "MainPS");
EXPECT_EQ(d3d12Fragment->profile, "ps_5_0");
EXPECT_NE(std::string(d3d12Fragment->sourceCode.CStr()).find("gAdditionalLights"), std::string::npos);
EXPECT_NE(std::string(d3d12Fragment->sourceCode.CStr()).find("gLightingParams"), std::string::npos);
EXPECT_EQ(std::string(d3d12Fragment->sourceCode.CStr()).find("#define XC_MAIN_LIGHT_SHADOWS 1"), std::string::npos);
@@ -1669,7 +1647,7 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
std::string(shadowD3D12Fragment->sourceCode.CStr()).find("#define XC_MAIN_LIGHT_SHADOWS 1"),
std::string::npos);
EXPECT_NE(
std::string(shadowD3D12Fragment->sourceCode.CStr()).find("gShadowMapTexture.Sample"),
std::string(shadowD3D12Fragment->sourceCode.CStr()).find("ShadowMapTexture.Sample"),
std::string::npos);
ShaderKeywordSet alphaShadowKeywords = {};
@@ -1695,13 +1673,13 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
ShaderBackend::OpenGL,
alphaShadowKeywords);
ASSERT_NE(alphaShadowOpenGLFragment, nullptr);
EXPECT_EQ(alphaShadowOpenGLFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(alphaShadowOpenGLFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(alphaShadowOpenGLFragment->entryPoint, "MainPS");
EXPECT_EQ(alphaShadowOpenGLFragment->profile, "ps_5_0");
const std::string alphaShadowOpenGLSource = alphaShadowOpenGLFragment->sourceCode.CStr();
EXPECT_NE(alphaShadowOpenGLSource.find("#version 430"), std::string::npos);
EXPECT_NE(alphaShadowOpenGLSource.find("#define XC_ALPHA_TEST 1"), std::string::npos);
EXPECT_LT(
alphaShadowOpenGLSource.find("#version 430"),
alphaShadowOpenGLSource.find("#define XC_ALPHA_TEST 1"));
EXPECT_NE(alphaShadowOpenGLSource.find("discard;"), std::string::npos);
EXPECT_NE(alphaShadowOpenGLSource.find("clip(baseColor.a - gAlphaCutoffParams.x);"), std::string::npos);
const ShaderStageVariant* alphaShadowVulkanFragment = shader->FindVariant(
"ForwardLit",
@@ -1709,19 +1687,23 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
ShaderBackend::Vulkan,
alphaShadowKeywords);
ASSERT_NE(alphaShadowVulkanFragment, nullptr);
EXPECT_EQ(alphaShadowVulkanFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(alphaShadowVulkanFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(alphaShadowVulkanFragment->entryPoint, "MainPS");
EXPECT_EQ(alphaShadowVulkanFragment->profile, "ps_5_0");
const std::string alphaShadowVulkanSource = alphaShadowVulkanFragment->sourceCode.CStr();
EXPECT_NE(alphaShadowVulkanSource.find("#version 450"), std::string::npos);
EXPECT_NE(alphaShadowVulkanSource.find("#define XC_ALPHA_TEST 1"), std::string::npos);
EXPECT_LT(
alphaShadowVulkanSource.find("#version 450"),
alphaShadowVulkanSource.find("#define XC_ALPHA_TEST 1"));
EXPECT_NE(alphaShadowVulkanSource.find("ShadowMapTexture.Sample"), std::string::npos);
const ShaderStageVariant* openglFragment = shader->FindVariant(
"ForwardLit",
ShaderType::Fragment,
ShaderBackend::OpenGL);
ASSERT_NE(openglFragment, nullptr);
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("XC_BUILTIN_FORWARD_LIT_OPENGL_PS"), std::string::npos);
EXPECT_EQ(openglFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(openglFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(openglFragment->entryPoint, "MainPS");
EXPECT_EQ(openglFragment->profile, "ps_5_0");
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("gAdditionalLights"), std::string::npos);
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("gLightingParams"), std::string::npos);
@@ -1730,14 +1712,17 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
ShaderType::Fragment,
ShaderBackend::Vulkan);
ASSERT_NE(vulkanFragment, nullptr);
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("XC_BUILTIN_FORWARD_LIT_VULKAN_PS"), std::string::npos);
EXPECT_EQ(vulkanFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(vulkanFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(vulkanFragment->entryPoint, "MainPS");
EXPECT_EQ(vulkanFragment->profile, "ps_5_0");
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("gAdditionalLights"), std::string::npos);
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("gLightingParams"), std::string::npos);
delete shader;
}
TEST(ShaderLoader, LoadBuiltinUnlitShaderBuildsBackendVariants) {
TEST(ShaderLoader, LoadBuiltinUnlitShaderBuildsUnityStyleSingleSourceVariants) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinUnlitShaderPath());
ASSERT_TRUE(result);
@@ -1750,9 +1735,13 @@ TEST(ShaderLoader, LoadBuiltinUnlitShaderBuildsBackendVariants) {
const ShaderPass* pass = shader->FindPass("Unlit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(shader->GetProperties().Size(), 2u);
ASSERT_EQ(pass->variants.Size(), 6u);
ASSERT_EQ(pass->variants.Size(), 2u);
ASSERT_EQ(pass->tags.Size(), 1u);
ASSERT_EQ(pass->resources.Size(), 4u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
EXPECT_EQ(pass->tags[0].name, "LightMode");
EXPECT_EQ(pass->tags[0].value, "Unlit");
@@ -1766,13 +1755,7 @@ TEST(ShaderLoader, LoadBuiltinUnlitShaderBuildsBackendVariants) {
EXPECT_EQ(baseMapProperty->type, ShaderPropertyType::Texture2D);
EXPECT_EQ(baseMapProperty->semantic, "BaseColorTexture");
const ShaderResourceBindingDesc* perObjectBinding =
shader->FindPassResourceBinding("Unlit", "PerObjectConstants");
ASSERT_NE(perObjectBinding, nullptr);
EXPECT_EQ(perObjectBinding->type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(perObjectBinding->set, 0u);
EXPECT_EQ(perObjectBinding->binding, 0u);
EXPECT_EQ(perObjectBinding->semantic, "PerObject");
EXPECT_EQ(shader->FindPassResourceBinding("Unlit", "PerObjectConstants"), nullptr);
EXPECT_NE(shader->FindVariant("Unlit", ShaderType::Vertex, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("Unlit", ShaderType::Fragment, ShaderBackend::D3D12), nullptr);
@@ -1786,26 +1769,38 @@ TEST(ShaderLoader, LoadBuiltinUnlitShaderBuildsBackendVariants) {
ShaderType::Vertex,
ShaderBackend::D3D12);
ASSERT_NE(d3d12Vertex, nullptr);
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("XC_BUILTIN_UNLIT_D3D12_VS"), std::string::npos);
EXPECT_EQ(d3d12Vertex->backend, ShaderBackend::Generic);
EXPECT_EQ(d3d12Vertex->language, ShaderLanguage::HLSL);
EXPECT_EQ(d3d12Vertex->entryPoint, "MainVS");
EXPECT_EQ(d3d12Vertex->profile, "vs_5_0");
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("gProjectionMatrix"), std::string::npos);
const ShaderStageVariant* openglFragment = shader->FindVariant(
"Unlit",
ShaderType::Fragment,
ShaderBackend::OpenGL);
ASSERT_NE(openglFragment, nullptr);
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("XC_BUILTIN_UNLIT_OPENGL_PS"), std::string::npos);
EXPECT_EQ(openglFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(openglFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(openglFragment->entryPoint, "MainPS");
EXPECT_EQ(openglFragment->profile, "ps_5_0");
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("BaseColorTexture.Sample"), std::string::npos);
const ShaderStageVariant* vulkanFragment = shader->FindVariant(
"Unlit",
ShaderType::Fragment,
ShaderBackend::Vulkan);
ASSERT_NE(vulkanFragment, nullptr);
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("XC_BUILTIN_UNLIT_VULKAN_PS"), std::string::npos);
EXPECT_EQ(vulkanFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(vulkanFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(vulkanFragment->entryPoint, "MainPS");
EXPECT_EQ(vulkanFragment->profile, "ps_5_0");
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("gBaseColorFactor"), std::string::npos);
delete shader;
}
TEST(ShaderLoader, LoadBuiltinObjectIdShaderBuildsBackendVariants) {
TEST(ShaderLoader, LoadBuiltinObjectIdShaderBuildsUnityStyleSingleSourceVariants) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinObjectIdShaderPath());
ASSERT_TRUE(result);
@@ -1817,12 +1812,12 @@ TEST(ShaderLoader, LoadBuiltinObjectIdShaderBuildsBackendVariants) {
const ShaderPass* pass = shader->FindPass("ObjectId");
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);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
ASSERT_EQ(pass->variants.Size(), 2u);
ASSERT_EQ(pass->tags.Size(), 1u);
EXPECT_EQ(pass->tags[0].name, "LightMode");
EXPECT_EQ(pass->tags[0].value, "ObjectId");
@@ -1839,26 +1834,38 @@ TEST(ShaderLoader, LoadBuiltinObjectIdShaderBuildsBackendVariants) {
ShaderType::Vertex,
ShaderBackend::D3D12);
ASSERT_NE(d3d12Vertex, nullptr);
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("XC_BUILTIN_OBJECT_ID_D3D12_VS"), std::string::npos);
EXPECT_EQ(d3d12Vertex->backend, ShaderBackend::Generic);
EXPECT_EQ(d3d12Vertex->language, ShaderLanguage::HLSL);
EXPECT_EQ(d3d12Vertex->entryPoint, "MainVS");
EXPECT_EQ(d3d12Vertex->profile, "vs_5_0");
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("gObjectIdColor"), std::string::npos);
const ShaderStageVariant* openglFragment = shader->FindVariant(
"ObjectId",
ShaderType::Fragment,
ShaderBackend::OpenGL);
ASSERT_NE(openglFragment, nullptr);
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("XC_BUILTIN_OBJECT_ID_OPENGL_PS"), std::string::npos);
EXPECT_EQ(openglFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(openglFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(openglFragment->entryPoint, "MainPS");
EXPECT_EQ(openglFragment->profile, "ps_5_0");
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("return gObjectIdColor;"), std::string::npos);
const ShaderStageVariant* vulkanFragment = shader->FindVariant(
"ObjectId",
ShaderType::Fragment,
ShaderBackend::Vulkan);
ASSERT_NE(vulkanFragment, nullptr);
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("XC_BUILTIN_OBJECT_ID_VULKAN_PS"), std::string::npos);
EXPECT_EQ(vulkanFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(vulkanFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(vulkanFragment->entryPoint, "MainPS");
EXPECT_EQ(vulkanFragment->profile, "ps_5_0");
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("gModelMatrix"), std::string::npos);
delete shader;
}
TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsBackendVariants) {
TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsUnityStyleSingleSourceVariants) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath());
ASSERT_TRUE(result);
@@ -1871,24 +1878,12 @@ TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsBackendVariants) {
const ShaderPass* pass = shader->FindPass("DepthOnly");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(shader->GetProperties().Size(), 3u);
ASSERT_EQ(pass->resources.Size(), 4u);
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);
EXPECT_EQ(pass->resources[1].semantic, "Material");
EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[1].set, 1u);
EXPECT_EQ(pass->resources[1].binding, 0u);
EXPECT_EQ(pass->resources[2].semantic, "BaseColorTexture");
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D);
EXPECT_EQ(pass->resources[2].set, 2u);
EXPECT_EQ(pass->resources[2].binding, 0u);
EXPECT_EQ(pass->resources[3].semantic, "LinearClampSampler");
EXPECT_EQ(pass->resources[3].type, ShaderResourceType::Sampler);
EXPECT_EQ(pass->resources[3].set, 3u);
EXPECT_EQ(pass->resources[3].binding, 0u);
ASSERT_EQ(pass->variants.Size(), 12u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
ASSERT_EQ(pass->variants.Size(), 4u);
ASSERT_EQ(pass->tags.Size(), 1u);
EXPECT_EQ(pass->tags[0].name, "LightMode");
EXPECT_EQ(pass->tags[0].value, "DepthOnly");
@@ -1897,6 +1892,27 @@ TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsBackendVariants) {
ASSERT_EQ(pass->keywordDeclarations[0].options.Size(), 2u);
EXPECT_EQ(pass->keywordDeclarations[0].options[0], "_");
EXPECT_EQ(pass->keywordDeclarations[0].options[1], "XC_ALPHA_TEST");
EXPECT_TRUE(shader->PassDeclaresKeyword("DepthOnly", "XC_ALPHA_TEST"));
const ShaderPropertyDesc* baseColorProperty = shader->FindProperty("_BaseColor");
ASSERT_NE(baseColorProperty, nullptr);
EXPECT_EQ(baseColorProperty->type, ShaderPropertyType::Color);
EXPECT_EQ(baseColorProperty->semantic, "BaseColor");
const ShaderPropertyDesc* cutoffProperty = shader->FindProperty("_Cutoff");
ASSERT_NE(cutoffProperty, nullptr);
EXPECT_EQ(cutoffProperty->type, ShaderPropertyType::Range);
EXPECT_EQ(cutoffProperty->semantic, "AlphaCutoff");
const ShaderPropertyDesc* baseMapProperty = shader->FindProperty("_MainTex");
ASSERT_NE(baseMapProperty, nullptr);
EXPECT_EQ(baseMapProperty->type, ShaderPropertyType::Texture2D);
EXPECT_EQ(baseMapProperty->semantic, "BaseColorTexture");
EXPECT_EQ(shader->FindPassResourceBinding("DepthOnly", "PerObjectConstants"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("DepthOnly", "MaterialConstants"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("DepthOnly", "BaseColorTexture"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("DepthOnly", "LinearClampSampler"), nullptr);
EXPECT_NE(shader->FindVariant("DepthOnly", ShaderType::Vertex, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("DepthOnly", ShaderType::Fragment, ShaderBackend::D3D12), nullptr);
@@ -1910,21 +1926,33 @@ TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsBackendVariants) {
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);
EXPECT_EQ(d3d12Vertex->backend, ShaderBackend::Generic);
EXPECT_EQ(d3d12Vertex->language, ShaderLanguage::HLSL);
EXPECT_EQ(d3d12Vertex->entryPoint, "MainVS");
EXPECT_EQ(d3d12Vertex->profile, "vs_5_0");
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("gProjectionMatrix"), 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);
EXPECT_EQ(openglFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(openglFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(openglFragment->entryPoint, "MainPS");
EXPECT_EQ(openglFragment->profile, "ps_5_0");
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("BaseColorTexture.Sample"), 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);
EXPECT_EQ(vulkanFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(vulkanFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(vulkanFragment->entryPoint, "MainPS");
EXPECT_EQ(vulkanFragment->profile, "ps_5_0");
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("gAlphaCutoffParams"), std::string::npos);
ShaderKeywordSet alphaKeywords = {};
alphaKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
@@ -1935,6 +1963,10 @@ TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsBackendVariants) {
ShaderBackend::D3D12,
alphaKeywords);
ASSERT_NE(alphaD3D12Fragment, nullptr);
EXPECT_EQ(alphaD3D12Fragment->backend, ShaderBackend::Generic);
EXPECT_EQ(alphaD3D12Fragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(alphaD3D12Fragment->entryPoint, "MainPS");
EXPECT_EQ(alphaD3D12Fragment->profile, "ps_5_0");
EXPECT_NE(
std::string(alphaD3D12Fragment->sourceCode.CStr()).find("#define XC_ALPHA_TEST 1"),
std::string::npos);
@@ -1948,18 +1980,18 @@ TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsBackendVariants) {
ShaderBackend::OpenGL,
alphaKeywords);
ASSERT_NE(alphaOpenGLFragment, nullptr);
EXPECT_EQ(alphaOpenGLFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(alphaOpenGLFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(alphaOpenGLFragment->entryPoint, "MainPS");
EXPECT_EQ(alphaOpenGLFragment->profile, "ps_5_0");
const std::string alphaOpenGLSource = alphaOpenGLFragment->sourceCode.CStr();
EXPECT_NE(alphaOpenGLSource.find("#version 430"), std::string::npos);
EXPECT_NE(alphaOpenGLSource.find("#define XC_ALPHA_TEST 1"), std::string::npos);
EXPECT_LT(
alphaOpenGLSource.find("#version 430"),
alphaOpenGLSource.find("#define XC_ALPHA_TEST 1"));
EXPECT_NE(alphaOpenGLSource.find("discard;"), std::string::npos);
EXPECT_NE(alphaOpenGLSource.find("clip(baseColor.a - gAlphaCutoffParams.x);"), std::string::npos);
delete shader;
}
TEST(ShaderLoader, LoadBuiltinShadowCasterShaderBuildsBackendVariants) {
TEST(ShaderLoader, LoadBuiltinShadowCasterShaderBuildsUnityStyleSingleSourceVariants) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinShadowCasterShaderPath());
ASSERT_TRUE(result);
@@ -1972,24 +2004,12 @@ TEST(ShaderLoader, LoadBuiltinShadowCasterShaderBuildsBackendVariants) {
const ShaderPass* pass = shader->FindPass("ShadowCaster");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(shader->GetProperties().Size(), 3u);
ASSERT_EQ(pass->resources.Size(), 4u);
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);
EXPECT_EQ(pass->resources[1].semantic, "Material");
EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[1].set, 1u);
EXPECT_EQ(pass->resources[1].binding, 0u);
EXPECT_EQ(pass->resources[2].semantic, "BaseColorTexture");
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D);
EXPECT_EQ(pass->resources[2].set, 2u);
EXPECT_EQ(pass->resources[2].binding, 0u);
EXPECT_EQ(pass->resources[3].semantic, "LinearClampSampler");
EXPECT_EQ(pass->resources[3].type, ShaderResourceType::Sampler);
EXPECT_EQ(pass->resources[3].set, 3u);
EXPECT_EQ(pass->resources[3].binding, 0u);
ASSERT_EQ(pass->variants.Size(), 12u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable);
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
ASSERT_EQ(pass->variants.Size(), 4u);
ASSERT_EQ(pass->tags.Size(), 1u);
EXPECT_EQ(pass->tags[0].name, "LightMode");
EXPECT_EQ(pass->tags[0].value, "ShadowCaster");
@@ -1998,6 +2018,27 @@ TEST(ShaderLoader, LoadBuiltinShadowCasterShaderBuildsBackendVariants) {
ASSERT_EQ(pass->keywordDeclarations[0].options.Size(), 2u);
EXPECT_EQ(pass->keywordDeclarations[0].options[0], "_");
EXPECT_EQ(pass->keywordDeclarations[0].options[1], "XC_ALPHA_TEST");
EXPECT_TRUE(shader->PassDeclaresKeyword("ShadowCaster", "XC_ALPHA_TEST"));
const ShaderPropertyDesc* baseColorProperty = shader->FindProperty("_BaseColor");
ASSERT_NE(baseColorProperty, nullptr);
EXPECT_EQ(baseColorProperty->type, ShaderPropertyType::Color);
EXPECT_EQ(baseColorProperty->semantic, "BaseColor");
const ShaderPropertyDesc* cutoffProperty = shader->FindProperty("_Cutoff");
ASSERT_NE(cutoffProperty, nullptr);
EXPECT_EQ(cutoffProperty->type, ShaderPropertyType::Range);
EXPECT_EQ(cutoffProperty->semantic, "AlphaCutoff");
const ShaderPropertyDesc* baseMapProperty = shader->FindProperty("_MainTex");
ASSERT_NE(baseMapProperty, nullptr);
EXPECT_EQ(baseMapProperty->type, ShaderPropertyType::Texture2D);
EXPECT_EQ(baseMapProperty->semantic, "BaseColorTexture");
EXPECT_EQ(shader->FindPassResourceBinding("ShadowCaster", "PerObjectConstants"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("ShadowCaster", "MaterialConstants"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("ShadowCaster", "BaseColorTexture"), nullptr);
EXPECT_EQ(shader->FindPassResourceBinding("ShadowCaster", "LinearClampSampler"), nullptr);
EXPECT_NE(shader->FindVariant("ShadowCaster", ShaderType::Vertex, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("ShadowCaster", ShaderType::Fragment, ShaderBackend::D3D12), nullptr);
@@ -2011,27 +2052,33 @@ TEST(ShaderLoader, LoadBuiltinShadowCasterShaderBuildsBackendVariants) {
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);
EXPECT_EQ(d3d12Vertex->backend, ShaderBackend::Generic);
EXPECT_EQ(d3d12Vertex->language, ShaderLanguage::HLSL);
EXPECT_EQ(d3d12Vertex->entryPoint, "MainVS");
EXPECT_EQ(d3d12Vertex->profile, "vs_5_0");
EXPECT_NE(std::string(d3d12Vertex->sourceCode.CStr()).find("gProjectionMatrix"), 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);
EXPECT_EQ(openglFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(openglFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(openglFragment->entryPoint, "MainPS");
EXPECT_EQ(openglFragment->profile, "ps_5_0");
EXPECT_NE(std::string(openglFragment->sourceCode.CStr()).find("return input.position.z;"), 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);
EXPECT_EQ(vulkanFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(vulkanFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(vulkanFragment->entryPoint, "MainPS");
EXPECT_EQ(vulkanFragment->profile, "ps_5_0");
EXPECT_NE(std::string(vulkanFragment->sourceCode.CStr()).find("gAlphaCutoffParams"), std::string::npos);
ShaderKeywordSet alphaKeywords = {};
alphaKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
@@ -2042,6 +2089,10 @@ TEST(ShaderLoader, LoadBuiltinShadowCasterShaderBuildsBackendVariants) {
ShaderBackend::D3D12,
alphaKeywords);
ASSERT_NE(alphaD3D12Fragment, nullptr);
EXPECT_EQ(alphaD3D12Fragment->backend, ShaderBackend::Generic);
EXPECT_EQ(alphaD3D12Fragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(alphaD3D12Fragment->entryPoint, "MainPS");
EXPECT_EQ(alphaD3D12Fragment->profile, "ps_5_0");
EXPECT_NE(
std::string(alphaD3D12Fragment->sourceCode.CStr()).find("#define XC_ALPHA_TEST 1"),
std::string::npos);
@@ -2055,13 +2106,13 @@ TEST(ShaderLoader, LoadBuiltinShadowCasterShaderBuildsBackendVariants) {
ShaderBackend::OpenGL,
alphaKeywords);
ASSERT_NE(alphaOpenGLFragment, nullptr);
EXPECT_EQ(alphaOpenGLFragment->backend, ShaderBackend::Generic);
EXPECT_EQ(alphaOpenGLFragment->language, ShaderLanguage::HLSL);
EXPECT_EQ(alphaOpenGLFragment->entryPoint, "MainPS");
EXPECT_EQ(alphaOpenGLFragment->profile, "ps_5_0");
const std::string alphaOpenGLSource = alphaOpenGLFragment->sourceCode.CStr();
EXPECT_NE(alphaOpenGLSource.find("#version 430"), std::string::npos);
EXPECT_NE(alphaOpenGLSource.find("#define XC_ALPHA_TEST 1"), std::string::npos);
EXPECT_LT(
alphaOpenGLSource.find("#version 430"),
alphaOpenGLSource.find("#define XC_ALPHA_TEST 1"));
EXPECT_NE(alphaOpenGLSource.find("discard;"), std::string::npos);
EXPECT_NE(alphaOpenGLSource.find("clip(baseColor.a - gAlphaCutoffParams.x);"), std::string::npos);
delete shader;
}
@@ -2125,19 +2176,19 @@ TEST(ShaderLoader, ResourceManagerLoadsBuiltinShadersOutsideProjectWorkingDirect
"ForwardLit",
ShaderType::Vertex,
ShaderBackend::D3D12,
"XC_BUILTIN_FORWARD_LIT_D3D12_VS");
"cbuffer LightingConstants");
expectBuiltinShader(
GetBuiltinUnlitShaderPath(),
"Unlit",
ShaderType::Fragment,
ShaderBackend::OpenGL,
"XC_BUILTIN_UNLIT_OPENGL_PS");
"BaseColorTexture");
expectBuiltinShader(
GetBuiltinObjectIdShaderPath(),
"ObjectId",
ShaderType::Fragment,
ShaderBackend::Vulkan,
"XC_BUILTIN_OBJECT_ID_VULKAN_PS");
"return gObjectIdColor;");
fs::current_path(previousPath);
manager.SetResourceRoot(previousResourceRoot);