Files
sbox-public/engine/Definitions/common/Render/renderdevice.def
Matt Stevens 1ed034cecd Shadows (#4109)
* Shadows Rewrite: C#, bindless, flexible, quality options, less VRAM...

Introduces a new shadow mapper written entirely in C#, as well as
rewritten shader code for sampling shadow maps. This fully removes and
replaces Source 2's native shadow mapping giving us greater flexibility
and allowing us to open-source it all.

The main goal for the new shadow mapper is greater flexibility whilst
reducing complexity.

Older shaders are incompatible with the new lighting buffers, and will
need to be recompiled to receive lighting properly.

What's new:

- Bindless per-light shadow maps instead of a shared shadow atlas — this
  means games can avoid the shadow atlas cost if not using many shadows,
  but also allows games with many shadows to not be limited by atlas
  space.

- Directional lights have developer configurable cascade count (1-4) and
  control over split ratio (logarithmic/uniform blend via lambda
  parameter), useful for games where you may not need multiple cascades.
  User quality settings define a maximum cascade count which always
  overrides developer settings, allowing low end hardware to use fewer.

- Directional lights have a dedicated cbuffer and uniform fast path in
  rendering code, and are no longer binned and looped over with local
  lights. Every pixel on screen is always affected by a directional
  light.

- CSM cascade selection uses bounding spheres instead of depth
  comparison, with per-cascade texel snapping to eliminate sub-texel
  shimmer.

- Point lights use a TextureCube for cube shadows for much simpler
  rendering and mapping, along with hardware PCF filtering.

- Local light shadow resolution is derived from each light's screen-space
  size. Shadows below a configurable threshold are not rendered at all.
  Lights are sorted by screen size, and r.shadows.max caps the total
  count, culling least important lights first.

- User settings have been added for shadow quality (Low/Medium/High)
  controlling max resolution, max cascades, and PCF filter quality.

- Local light shadow maps use D16 depth format, halving memory compared
  to D32. CSMs remain D32 for precision at large distances.
  (Although this could be a TODO, I bet we could make it work in D16)

- ShadowHardness: New per-light property controlling shadow sharpness.
  Defaults to soft (0.0) and scales up to 4x sharper. For directional
  lights, hardness is automatically scaled per cascade proportional to
  texel density (wider cascades get softer shadows), and clamped so the
  filter never exceeds a full texel — ensuring consistent softness
  across cascade transitions.

- Shadow debug overlay showing all information about allocated shadow maps,
  their textures, cascades and more.

- Many new convars to control
  - r.shadows.max: Maximum number of shadow-casting local lights, sorted by screen size, least important culled first
  - r.shadows.maxresolution: Max texture size for a projected light shadow map (128–4096)
  - r.shadows.quality: Shadow filter quality (0=Off, 1=Low, 2=Med, 3=High, 4=Experimental Penumbra)
  - r.shadows.csm.maxcascades: Maximum number of cascades for directional light shadows (1–4)
  - r.shadows.csm.maxresolution: Maximum resolution for each cascade shadow map (512–8192)
  - r.shadows.csm.distance: Maximum distance from camera that directional light shadows render (500–50000)
  - r.shadows.debug: Show shadow debug overlay with CSM textures, cascade bounds, and memory budget
  - r.shadows.csm.enabled: Enable or disable directional light (CSM) shadows
  - r.shadows.local.enabled: Enable or disable local light (spot/point) shadows
  - r.shadows.depthbias: Rasterizer constant depth bias during shadow map rendering
  - r.shadows.slopescale: Rasterizer slope-scaled depth bias during shadow map rendering
  - r.shadows.size_cull_threshold: Screen size percentage below which local light shadows are culled

- SceneLight refactored into a base class with ScenePointLight,
  SceneSpotLight, SceneDirectionalLight. SceneOrthoLight removed.

- Simplified Light.hlsl: Light is now a class, DynamicLight merged into
  Light, ProbeLight and LightmappedLight no longer inherit from
  DynamicLight.

- GPULight/BinnedLight struct reorganized and trimmed: explicit typed
  fields instead of packed uint4 Params, shadow data replaced with a
  shadow index into a separate StructuredBuffer, removed embedded shadow
  matrices and atlas bounds.

- ViewLightingConfig cleaned up: removed ViewLightingFlags,
  Shadow3x3PCFConstants, EnvironmentMapSizeConstants,
  LegacyAmbientLightColor.

- Baked light mode flags fixed: BAKED lights (lightmaps only) no longer
  create shadow maps. MIXED_SHADOWS gated to Stationary lights only
  (was unconditionally applied). RENDER_ALL_GEOMETRY flag removed.
  DirectLightMode enum documented across Hammer entity definitions.

- Removed light cookie sheets; cookie UV derived from light transform
  (LightToWorld transpose). Cookie sampling simplified to a single
  bindless texture lookup.
2026-03-27 12:07:24 +00:00

194 lines
6.6 KiB
Modula-2

#include "rendersystem/sheet.h"
native struct TextureCreationConfig_t is NativeEngine.TextureCreationConfig_t;
native struct CTextureDesc is NativeEngine.CTextureDesc;
native enum RenderDeviceAPI_t is NativeEngine.RenderDeviceAPI_t;
native pointer SwapChainHandle_t;
native pointer RenderBufferHandle_t is NativeEngine.RenderBufferHandle_t;
native pointer RenderInputLayout_t is NativeEngine.RenderInputLayout_t;
native struct BufferDesc_t is NativeEngine.BufferDesc;
native enum RenderBufferType_t is NativeEngine.RenderBufferType;
native enum RenderBufferFlags_t is NativeEngine.RenderBufferFlags;
native enum DeviceSpecificInfo_t is NativeEngine.DeviceSpecificInfo_t;
native struct VulkanDeviceSpecificTexture_t is NativeEngine.VulkanDeviceSpecificTexture_t;
native struct RenderDeviceInfo_t;
native pointer SamplerStateHandle_t;
native struct CSamplerStateDesc is NativeEngine.CSamplerStateDesc;
native accessor g_pRenderDevice
{
SamplerStateHandle_t FindOrCreateSamplerState(CSamplerStateDesc samplerDesc );
int GetSamplerIndex( SamplerStateHandle_t samplerState );
RenderDeviceInfo_t GetSwapChainInfo( SwapChainHandle_t swapChain );
ITexture FindOrCreateFileTexture( string pFileName, RenderSystemAssetFileLoadMode nLoadMode );
ITexture FindOrCreateTexture2( string pResourceName, bool bIsAnonymous, TextureCreationConfig_t pDescriptor, void* data, int dataSize );
void ClearTexture( ITexture hTexture, Color color );
void AsyncSetTextureData2( ITexture hTexture, void* pData, int nDataSize, Rect3D rect );
ITexture GetSwapChainTexture( SwapChainHandle_t swapChain, SwapChainBuffer bufferType );
bool GetGPUFrameTimeMS( SwapChainHandle_t swapChain, out float pGPUFrameTimeMSOut, out uint pFrameNumberOut );
asref cast CTextureDesc GetTextureDesc( ITexture hTexture );
asref cast CTextureDesc GetOnDiskTextureDesc( ITexture hTexture );
RenderMultisampleType GetTextureMultisampleType( ITexture hTexture );
inline uint ComputeTextureMemorySize( ITexture hTexture )
{
const CTextureDesc *pDesc = g_pRenderDevice->GetTextureDesc( hTexture );
return g_pRenderDevice->ComputeTextureMemorySize( *pDesc );
}
IRenderContext CreateRenderContext( uint flags );
void ReleaseRenderContext( IRenderContext context );
bool ReadTexturePixels( ITexture hTexture, ref Rect_t pSrcRect, int nSrcSlice, int nSrcMip, ref Rect_t pDstRect, void* pData, ImageFormat dstFormat, int nDstStride );
void DestroySwapChain( SwapChainHandle_t hSwapChain );
bool Present( SwapChainHandle_t chain );
void Flush();
void ForceFlushGPU( SwapChainHandle_t hSwapChain );
bool CanRenderToSwapChain( SwapChainHandle_t chain );
bool IsUsing32BitDepthBuffer();
inline Vector2 GetBackbufferDimensions( SwapChainHandle_t chain )
{
int nBBWidth, nBBHeight;
g_pRenderDevice->GetBackBufferDimensions( chain, &nBBWidth, &nBBHeight );
return Vector2D( (float)nBBWidth, (float)nBBHeight );
}
RenderShaderHandle_t CompileAndCreateShader( RenderShaderType nType, string pProgram, uint nBufLen, string pShaderVersion, string pDebugName );
int GetTextureLastUsed( ITexture hTexture );
void UnThrottleTextureStreamingForNFrames( uint nNumberOfFramesForUnthrottledTextureLoading );
int GetNumTextureLoadsInFlight();
void SetForcePreloadStreamingData( bool bForcePreloadStreamingData );
RenderDeviceAPI_t GetRenderDeviceAPI();
void MarkTextureUsed( ITexture texture, int nRequiredMipSize );
bool IsTextureRenderTarget( ITexture texture );
bool IsRayTracingSupported();
inline RenderBufferHandle_t CreateGPUBuffer( RenderBufferType_t nType, BufferDesc_t desc, RenderBufferFlags_t usage, string pDebugName )
{
desc.m_pDebugName = pDebugName;
return g_pRenderDevice->CreateGPUBuffer( nType, desc, usage );
}
void DestroyGPUBuffer( RenderBufferHandle_t hGPUBuffer );
bool ReadBuffer( RenderBufferHandle_t hBuffer, uint nOffsetInBytes, void* pBuf, uint nBytesToRead );
IntPtr GetDeviceSpecificInfo( DeviceSpecificInfo_t info );
IntPtr GetGraphicsAPISpecificTextureHandle( ITexture hTexture );
IntPtr GetDeviceSpecificTexture( ITexture hTexture );
int GetTextureViewIndex( ITexture hTexture, CastTo[RenderColorSpace_t] byte colorSpace, RenderTextureDimension_t dim );
inline void GetTextureResidencyInfo( CUtlVectorTexture pTextures, CUtlVectorString pNames )
{
TextureResidencyStats_t textureResidencyStats;
CUtlVector<TextureResidencyInfo_t> rawTextures;
g_pRenderDevice->GetTextureResidencyInfo( rawTextures, textureResidencyStats );
pTextures->EnsureCapacity( rawTextures.Count() );
pNames->EnsureCapacity( rawTextures.Count() );
for ( const TextureResidencyInfo_t &info : rawTextures )
{
if ( info.m_hTexture.IsLoaded() == false ) continue;
CPathBufferString texName;
info.m_hTexture.GetResourceName( &texName );
pTextures->AddToTail( info.m_hTexture );
pNames->AddToTail( texName );
}
}
inline Vector4 GetSheetInfo( ITexture texture )
{
Vector4D vec(0, 0, 0, 0 );
Vector2D vSheetSequenceDataUVCoords;
HRenderTexture hSequenceDataTexture;
int nSequenceCount;
if ( g_pRenderDevice->GetSheetSequenceTextureData( texture, &hSequenceDataTexture, &vSheetSequenceDataUVCoords, &nSequenceCount ) )
{
vec[0] = vSheetSequenceDataUVCoords.y;
const CTextureDesc *pTexHeader = g_pRenderDevice->GetTextureDesc( hSequenceDataTexture );
if ( pTexHeader )
{
vec[1] = pTexHeader->m_nWidth;
vec[2] = 1.0f / pTexHeader->m_nHeight;
vec[3] = (float)nSequenceCount;
}
}
return vec;
}
inline int GetSequenceCount( ITexture texture )
{
const Sheet_t *pSheet = g_pRenderDevice->GetTextureSheet( texture );
if ( pSheet == nullptr ) return 0;
return pSheet->m_Sequences.Count();
}
inline SheetSequence_t GetSequence( ITexture texture, int index )
{
const Sheet_t *pSheet = g_pRenderDevice->GetTextureSheet( texture );
if ( pSheet == nullptr ) return 0;
return &pSheet->m_Sequences[index];
}
}
native static class ImageLoader as NativeEngine.ImageLoader
{
int GetMemRequired( int width, int height, int depth, ImageFormat imageFormat, bool mipmap );
int GetMemRequired( int width, int height, int depth, int mipmaps, ImageFormat imageFormat );
bool ConvertImageFormat( CastTo[uint8*] void* src, ImageFormat srcImageFormat, CastTo[uint8*] void* dst, ImageFormat dstImageFormat, int width, int height, int srcStride, int dstStride );
}
native class SheetSequence_t
{
uint m_nId;
bool m_bClamp;
bool m_bAlphaCrop;
bool m_bNoColor;
bool m_bNoAlpha;
float m_flTotalTime;
inline int FrameCount()
{
return self->m_Frames.Count();
}
}
native class SheetSequenceFrame_t
{
float m_flDisplayTime;
inline int ImageCount()
{
return self->m_Images.Count();
}
inline SheetFrameImage_t GetImage( int i )
{
return self->m_Images[i];
}
}
native struct SheetFrameImage_t;