Simplify shader code for point/spot lights to use permutations instead

This commit is contained in:
2026-05-06 12:59:35 +02:00
parent 000d786d49
commit 650fa781d3
12 changed files with 61 additions and 135 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -10,6 +10,7 @@ template<int Size>
class GPUPipelineStatePermutations
{
public:
enum { StaticSize = Size };
GPUPipelineState* States[Size];
public:
@@ -97,13 +98,13 @@ public:
}
public:
bool Create(GPUPipelineState::Description& desc, GPUShader* shader, const StringAnsiView& psName)
bool Create(GPUPipelineState::Description& desc, GPUShader* shader, const StringAnsiView& psName, int32 permutationOffset = 0)
{
for (int i = 0; i < Size; i++)
{
ASSERT(Base::States[i]);
desc.PS = shader->GetPS(psName, i);
desc.PS = shader->GetPS(psName, i + permutationOffset);
if (Base::States[i]->Init(desc))
return true;
}
+9 -29
View File
@@ -31,10 +31,8 @@ bool LightPass::Init()
{
// Create pipeline states
_psLightDir.CreatePipelineStates();
_psLightPoint.CreatePipelineStates();
_psLightPointInside.CreatePipelineStates();
_psLightSpot.CreatePipelineStates();
_psLightSpotInside.CreatePipelineStates();
_psLightLocal.CreatePipelineStates();
_psLightLocalInside.CreatePipelineStates();
_psLightSky = GPUDevice::Instance->CreatePipelineState();
_psLightSkyInside = GPUDevice::Instance->CreatePipelineState();
_depthBounds = GPUDevice::Instance->Limits.HasDepthBounds && GPUDevice::Instance->Limits.HasReadOnlyDepth;
@@ -81,7 +79,7 @@ bool LightPass::setupResources()
if (_psLightDir.Create(psDesc, shader, "PS_Directional"))
return true;
}
if (!_psLightPoint.IsValid())
if (!_psLightLocal.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.BlendMode = BlendingMode::Add;
@@ -90,27 +88,11 @@ bool LightPass::setupResources()
psDesc.DepthEnable = true;
psDesc.DepthBoundsEnable = _depthBounds;
psDesc.CullMode = CullMode::Normal;
if (_psLightPoint.Create(psDesc, shader, "PS_Point"))
if (_psLightLocal.Create(psDesc, shader, "PS_LocalLight"))
return true;
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psLightPointInside.Create(psDesc, shader, "PS_Point"))
return true;
}
if (!_psLightSpot.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.BlendMode = BlendingMode::Add;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.DepthEnable = true;
psDesc.DepthBoundsEnable = _depthBounds;
psDesc.CullMode = CullMode::Normal;
if (_psLightSpot.Create(psDesc, shader, "PS_Spot"))
return true;
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psLightSpotInside.Create(psDesc, shader, "PS_Spot"))
if (_psLightLocalInside.Create(psDesc, shader, "PS_LocalLight"))
return true;
}
if (!_psLightSky->IsValid())
@@ -141,10 +123,8 @@ void LightPass::Dispose()
// Cleanup
_psLightDir.Delete();
_psLightPoint.Delete();
_psLightPointInside.Delete();
_psLightSpot.Delete();
_psLightSpotInside.Delete();
_psLightLocal.Delete();
_psLightLocalInside.Delete();
SAFE_DELETE_GPU_RESOURCE(_psLightSky);
SAFE_DELETE_GPU_RESOURCE(_psLightSkyInside);
SAFE_DELETE_GPU_RESOURCE(_psClearDiffuse);
@@ -301,7 +281,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
context->BindCB(0, cb0);
context->BindCB(1, cb1);
int32 permutationIndex = (disableSpecular ? 1 : 0) + (useIES ? 2 : 0);
context->SetState((isViewInside ? _psLightPointInside : _psLightPoint).Get(permutationIndex));
context->SetState((isViewInside ? _psLightLocalInside : _psLightLocal).Get(permutationIndex));
sphereMesh.Render(context);
}
@@ -349,7 +329,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
context->BindCB(0, cb0);
context->BindCB(1, cb1);
int32 permutationIndex = (disableSpecular ? 1 : 0) + (useIES ? 2 : 0);
context->SetState((isViewInside ? _psLightSpotInside : _psLightSpot).Get(permutationIndex));
context->SetState((isViewInside ? _psLightLocalInside : _psLightLocal).Get(permutationIndex));
sphereMesh.Render(context);
}
+4 -8
View File
@@ -16,10 +16,8 @@ class LightPass : public RendererPass<LightPass>
private:
AssetReference<Shader> _shader;
GPUPipelineStatePermutationsPs<2> _psLightDir;
GPUPipelineStatePermutationsPs<4> _psLightPoint;
GPUPipelineStatePermutationsPs<4> _psLightPointInside;
GPUPipelineStatePermutationsPs<4> _psLightSpot;
GPUPipelineStatePermutationsPs<4> _psLightSpotInside;
GPUPipelineStatePermutationsPs<4> _psLightLocal;
GPUPipelineStatePermutationsPs<4> _psLightLocalInside;
GPUPipelineState* _psLightSky = nullptr;
GPUPipelineState* _psLightSkyInside = nullptr;
GPUPipelineState* _psClearDiffuse = nullptr;
@@ -45,10 +43,8 @@ private:
void OnShaderReloading(Asset* obj)
{
_psLightDir.Release();
_psLightPoint.Release();
_psLightPointInside.Release();
_psLightSpot.Release();
_psLightSpotInside.Release();
_psLightLocal.Release();
_psLightLocalInside.Release();
_psLightSky->ReleaseGPU();
_psLightSkyInside->ReleaseGPU();
invalidateResources();
+5 -4
View File
@@ -525,6 +525,7 @@ bool ShadowsPass::setupResources()
if (_psShadowDir.Create(psDesc, shader, "PS_DirLight"))
return true;
}
StringAnsiView psLocalLight("PS_LocalLight");
if (!_psShadowPoint.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
@@ -533,11 +534,11 @@ bool ShadowsPass::setupResources()
psDesc.DepthEnable = true;
psDesc.DepthBoundsEnable = _depthBounds;
psDesc.CullMode = CullMode::Normal;
if (_psShadowPoint.Create(psDesc, shader, "PS_PointLight"))
if (_psShadowPoint.Create(psDesc, shader, psLocalLight))
return true;
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psShadowPointInside.Create(psDesc, shader, "PS_PointLight"))
if (_psShadowPointInside.Create(psDesc, shader, psLocalLight))
return true;
}
if (!_psShadowSpot.IsValid())
@@ -548,11 +549,11 @@ bool ShadowsPass::setupResources()
psDesc.DepthEnable = true;
psDesc.DepthBoundsEnable = _depthBounds;
psDesc.CullMode = CullMode::Normal;
if (_psShadowSpot.Create(psDesc, shader, "PS_SpotLight"))
if (_psShadowSpot.Create(psDesc, shader, psLocalLight, 8))
return true;
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psShadowSpotInside.Create(psDesc, shader, "PS_SpotLight"))
if (_psShadowSpotInside.Create(psDesc, shader, psLocalLight, 8))
return true;
}
if (_psDepthClear == nullptr)
+1 -1
View File
@@ -209,7 +209,7 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target
// Calculate lighting
#if RADIAL_LIGHT
bool isSpotLight = Light.SpotAngles.x > -2.0f;
bool isSpotLight = IsSpotLight(Light);
#else
bool isSpotLight = false;
#endif
+5
View File
@@ -58,6 +58,11 @@ struct LightSample
float3 Transmission;
};
bool IsSpotLight(LightData lightData)
{
return lightData.SpotAngles.x > -2.0f;
}
// Calculates radial light (point or spot) attenuation factors (distance, spot and radius mask)
void GetRadialLightAttenuation(
LightData lightData,
+3 -44
View File
@@ -70,13 +70,13 @@ void PS_Directional(Quad_VS2PS input, out float4 output : SV_Target0)
output = GetLighting(gBufferData.ViewPos, Light, gBuffer, shadowMask, false, false);
}
// Pixel shader for point light rendering
// Pixel shader for point/spot light rendering
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_2(LIGHTING_NO_SPECULAR=0, USE_IES_PROFILE=0)
META_PERMUTATION_2(LIGHTING_NO_SPECULAR=1, USE_IES_PROFILE=0)
META_PERMUTATION_2(LIGHTING_NO_SPECULAR=0, USE_IES_PROFILE=1)
META_PERMUTATION_2(LIGHTING_NO_SPECULAR=1, USE_IES_PROFILE=1)
void PS_Point(Model_VS2PS input, out float4 output : SV_Target0)
void PS_LocalLight(Model_VS2PS input, out float4 output : SV_Target0)
{
output = 0;
@@ -103,48 +103,7 @@ void PS_Point(Model_VS2PS input, out float4 output : SV_Target0)
}
// Calculate lighting
output = GetLighting(gBufferData.ViewPos, Light, gBuffer, shadowMask, true, false);
// Apply IES texture
#if USE_IES_PROFILE
output *= ComputeLightProfileMultiplier(IESTexture, gBuffer.WorldPos, Light.Position, -Light.Direction);
#endif
}
// Pixel shader for spot light rendering
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_2(LIGHTING_NO_SPECULAR=0, USE_IES_PROFILE=0)
META_PERMUTATION_2(LIGHTING_NO_SPECULAR=1, USE_IES_PROFILE=0)
META_PERMUTATION_2(LIGHTING_NO_SPECULAR=0, USE_IES_PROFILE=1)
META_PERMUTATION_2(LIGHTING_NO_SPECULAR=1, USE_IES_PROFILE=1)
void PS_Spot(Model_VS2PS input, out float4 output : SV_Target0)
{
output = 0;
// Obtain UVs corresponding to the current pixel
float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5);
// Sample GBuffer
GBufferData gBufferData = GetGBufferData();
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
// Check if cannot shadow pixel
BRANCH
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT)
{
discard;
return;
}
// Sample shadow mask
float4 shadowMask = 1;
if (Light.ShadowsBufferAddress != 0)
{
shadowMask = SAMPLE_RT(Shadow, uv);
}
// Calculate lighting
output = GetLighting(gBufferData.ViewPos, Light, gBuffer, shadowMask, true, true);
output = GetLighting(gBufferData.ViewPos, Light, gBuffer, shadowMask, true, IsSpotLight(Light));
// Apply IES texture
#if USE_IES_PROFILE
+24 -40
View File
@@ -70,36 +70,6 @@ Model_VS2PS VS_Model(ModelInput_PosOnly input)
return output;
}
// Pixel shader for point light shadow rendering
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0)
META_PERMUTATION_2(SHADOWS_QUALITY=1,CONTACT_SHADOWS=0)
META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=0)
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=0)
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=1)
META_PERMUTATION_2(SHADOWS_QUALITY=1,CONTACT_SHADOWS=1)
META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1)
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1)
float4 PS_PointLight(Model_VS2PS input) : SV_Target0
{
// Obtain texture coordinates corresponding to the current pixel
float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5);
// Sample GBuffer
GBufferData gBufferData = GetGBufferData();
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
// Sample shadow
ShadowSample shadow = SamplePointLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
#if CONTACT_SHADOWS && SHADOWS_QUALITY > 0
// Calculate screen-space contact shadow
shadow.SurfaceShadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
#endif
return GetShadowMask(shadow);
}
// Pixel shader for directional light shadow rendering
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_3(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0,SHADOWS_CSM_BLENDING=0)
@@ -135,17 +105,25 @@ float4 PS_DirLight(Quad_VS2PS input) : SV_Target0
return GetShadowMask(shadow);
}
// Pixel shader for spot light shadow rendering
// Pixel shader for point/spot light shadow rendering
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0)
META_PERMUTATION_2(SHADOWS_QUALITY=1,CONTACT_SHADOWS=0)
META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=0)
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=0)
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=1)
META_PERMUTATION_2(SHADOWS_QUALITY=1,CONTACT_SHADOWS=1)
META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1)
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1)
float4 PS_SpotLight(Model_VS2PS input) : SV_Target0
META_PERMUTATION_3(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0,LIGHT_TYPE=0) // Point light
META_PERMUTATION_3(SHADOWS_QUALITY=1,CONTACT_SHADOWS=0,LIGHT_TYPE=0)
META_PERMUTATION_3(SHADOWS_QUALITY=2,CONTACT_SHADOWS=0,LIGHT_TYPE=0)
META_PERMUTATION_3(SHADOWS_QUALITY=3,CONTACT_SHADOWS=0,LIGHT_TYPE=0)
META_PERMUTATION_3(SHADOWS_QUALITY=0,CONTACT_SHADOWS=1,LIGHT_TYPE=0)
META_PERMUTATION_3(SHADOWS_QUALITY=1,CONTACT_SHADOWS=1,LIGHT_TYPE=0)
META_PERMUTATION_3(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1,LIGHT_TYPE=0)
META_PERMUTATION_3(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1,LIGHT_TYPE=0)
META_PERMUTATION_3(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0,LIGHT_TYPE=1) // Spot light
META_PERMUTATION_3(SHADOWS_QUALITY=1,CONTACT_SHADOWS=0,LIGHT_TYPE=1)
META_PERMUTATION_3(SHADOWS_QUALITY=2,CONTACT_SHADOWS=0,LIGHT_TYPE=1)
META_PERMUTATION_3(SHADOWS_QUALITY=3,CONTACT_SHADOWS=0,LIGHT_TYPE=1)
META_PERMUTATION_3(SHADOWS_QUALITY=0,CONTACT_SHADOWS=1,LIGHT_TYPE=1)
META_PERMUTATION_3(SHADOWS_QUALITY=1,CONTACT_SHADOWS=1,LIGHT_TYPE=1)
META_PERMUTATION_3(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1,LIGHT_TYPE=1)
META_PERMUTATION_3(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1,LIGHT_TYPE=1)
float4 PS_LocalLight(Model_VS2PS input) : SV_Target0
{
// Obtain texture coordinates corresponding to the current pixel
float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5);
@@ -155,7 +133,13 @@ float4 PS_SpotLight(Model_VS2PS input) : SV_Target0
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
// Sample shadow
#if LIGHT_TYPE == 0
ShadowSample shadow = SamplePointLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
#elif LIGHT_TYPE == 1
ShadowSample shadow = SampleSpotLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
#else
ShadowSample shadow = (ShadowSample)0;
#endif
#if CONTACT_SHADOWS && SHADOWS_QUALITY > 0
// Calculate screen-space contact shadow
+1 -1
View File
@@ -175,7 +175,7 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0
uint samplesCount = historyAlpha < 0.01f ? MissedHistorySamplesCount : 1;
float NoL = 0;
bool isSpotLight = LocalLight.SpotAngles.x > -2.0f;
bool isSpotLight = IsSpotLight(LocalLight);
float4 scattering = 0;
for (uint sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++)
{