Files
UnrealEngine/Engine/Source/Runtime/RHI/Public/BoundShaderStateCache.h
2025-05-18 13:04:45 +08:00

297 lines
9.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
BoundShaderStateCache.h: Bound shader state cache definition.
=============================================================================*/
#pragma once
#include "CoreMinimal.h"
#include "RHI.h"
/**
* Key used to map a set of unique decl/vs/ps combinations to
* a vertex shader resource
*/
class FBoundShaderStateKey
{
public:
/** Initialization constructor. */
FBoundShaderStateKey(
FRHIVertexDeclaration* InVertexDeclaration,
FRHIVertexShader* InVertexShader,
FRHIPixelShader* InPixelShader,
FRHIGeometryShader* InGeometryShader = nullptr
)
: VertexDeclaration(InVertexDeclaration)
, VertexShader(InVertexShader)
, PixelShader(InPixelShader)
, GeometryShader(InGeometryShader)
{}
/** Initialization constructor. */
FBoundShaderStateKey(
FRHIMeshShader* InMeshShader,
FRHIAmplificationShader* InAmplificationShader,
FRHIPixelShader* InPixelShader
)
: MeshShader(InMeshShader)
, AmplificationShader(InAmplificationShader)
, PixelShader(InPixelShader)
{}
/**
* Get the RHI shader for the given frequency.
*/
FORCEINLINE FRHIVertexShader* GetVertexShader() const { return VertexShader; }
FORCEINLINE FRHIMeshShader* GetMeshShader() const { return MeshShader; }
FORCEINLINE FRHIAmplificationShader* GetAmplificationShader() const { return AmplificationShader; }
FORCEINLINE FRHIPixelShader* GetPixelShader() const { return PixelShader; }
FORCEINLINE FRHIGeometryShader* GetGeometryShader() const { return GeometryShader; }
/**
* Get the RHI vertex declaration.
*/
FORCEINLINE FRHIVertexDeclaration* GetVertexDeclaration() const { return VertexDeclaration; }
private:
/**
* Note: We intentionally do use ...Ref, not ...ParamRef to get
* AddRef() for object to prevent and rare issue that happened before.
* When changing and recompiling a shader it got the same memory
* pointer and because the caching is happening with pointer comparison
* the BoundShaderstate cache was holding on to the old pointer
* it was not creating a new entry.
*/
/** vertex decl for this combination */
FVertexDeclarationRHIRef VertexDeclaration;
/** vs for this combination */
FVertexShaderRHIRef VertexShader;
/** ms for this combination */
FMeshShaderRHIRef MeshShader;
/** as for this combination */
FAmplificationShaderRHIRef AmplificationShader;
/** ps for this combination */
FPixelShaderRHIRef PixelShader;
/** gs for this combination */
FGeometryShaderRHIRef GeometryShader;
friend class FBoundShaderStateLookupKey;
};
// Non-reference-counted version of shader state key.
// This structure is used as the actual key type for TMap, which avoids reference counting overhead during lookup.
// Note that FCachedBoundShaderStateLink contains a full-fat reference-counted FBoundShaderStateKey, ensuring
// correct lifetime management.
class FBoundShaderStateLookupKey
{
public:
// Note: implicit cast is allowed/expected for this constructor
FBoundShaderStateLookupKey(const FBoundShaderStateKey& Key)
: VertexDeclaration(Key.VertexDeclaration.GetReference())
, VertexShader(Key.VertexShader.GetReference())
, MeshShader(Key.MeshShader.GetReference())
, AmplificationShader(Key.AmplificationShader.GetReference())
, PixelShader(Key.PixelShader.GetReference())
, GeometryShader(Key.GeometryShader.GetReference())
{}
FBoundShaderStateLookupKey(
FRHIVertexDeclaration* InVertexDeclaration,
FRHIVertexShader* InVertexShader,
FRHIPixelShader* InPixelShader,
FRHIGeometryShader* InGeometryShader = nullptr
)
: VertexDeclaration(InVertexDeclaration)
, VertexShader(InVertexShader)
, PixelShader(InPixelShader)
, GeometryShader(InGeometryShader)
{}
FBoundShaderStateLookupKey(
FRHIMeshShader* InMeshShader,
FRHIAmplificationShader* InAmplificationShader,
FRHIPixelShader* InPixelShader
)
: MeshShader(InMeshShader)
, AmplificationShader(InAmplificationShader)
, PixelShader(InPixelShader)
{}
/**
* Equality is based on decl, vertex shader and pixel shader
* @param Other - instance to compare against
* @return true if equal
*/
friend bool operator == (const FBoundShaderStateLookupKey& A, const FBoundShaderStateLookupKey& B)
{
return A.VertexDeclaration == B.VertexDeclaration &&
A.VertexShader == B.VertexShader &&
A.MeshShader == B.MeshShader &&
A.AmplificationShader == B.AmplificationShader &&
A.PixelShader == B.PixelShader &&
A.GeometryShader == B.GeometryShader;
}
/**
* Get the hash for this type.
* @param Key - struct to hash
* @return dword hash based on type
*/
friend uint32 GetTypeHash(const FBoundShaderStateLookupKey& Key)
{
return GetTypeHash(Key.VertexDeclaration) ^
GetTypeHash(Key.VertexShader) ^
GetTypeHash(Key.MeshShader) ^
GetTypeHash(Key.AmplificationShader) ^
GetTypeHash(Key.PixelShader) ^
GetTypeHash(Key.GeometryShader);
}
private:
const FRHIVertexDeclaration* VertexDeclaration = nullptr;
const FRHIVertexShader* VertexShader = nullptr;
const FRHIMeshShader* MeshShader = nullptr;
const FRHIAmplificationShader* AmplificationShader = nullptr;
const FRHIPixelShader* PixelShader = nullptr;
const FRHIGeometryShader* GeometryShader = nullptr;
};
/**
* Encapsulates a bound shader state's entry in the cache.
* Handles removal from the bound shader state cache on destruction.
* RHIs that use cached bound shader states should create one for each bound shader state.
*/
class FCachedBoundShaderStateLink
{
public:
/**
* The cached bound shader state. This is not a reference counted pointer because we rely on the RHI to destruct this object
* when the bound shader state this references is destructed.
*/
FRHIBoundShaderState* BoundShaderState;
/** Adds the bound shader state to the cache. */
RHI_API FCachedBoundShaderStateLink(
FRHIVertexDeclaration* VertexDeclaration,
FRHIVertexShader* VertexShader,
FRHIPixelShader* PixelShader,
FRHIBoundShaderState* InBoundShaderState,
bool bAddToSingleThreadedCache = true
);
/** Adds the bound shader state to the cache. */
RHI_API FCachedBoundShaderStateLink(
FRHIVertexDeclaration* VertexDeclaration,
FRHIVertexShader* VertexShader,
FRHIPixelShader* PixelShader,
FRHIGeometryShader* GeometryShader,
FRHIBoundShaderState* InBoundShaderState,
bool bAddToSingleThreadedCache = true
);
/** Adds the bound shader state to the cache. */
RHI_API FCachedBoundShaderStateLink(
FRHIMeshShader* MeshShader,
FRHIAmplificationShader* AmplificationShader,
FRHIPixelShader* PixelShader,
FRHIBoundShaderState* InBoundShaderState,
bool bAddToSingleThreadedCache = true
);
/** Destructor. Removes the bound shader state from the cache. */
RHI_API ~FCachedBoundShaderStateLink();
/**
* Get the RHI shader for the given frequency.
*/
FORCEINLINE FRHIVertexShader* GetVertexShader() const { return Key.GetVertexShader(); }
FORCEINLINE FRHIMeshShader* GetMeshShader() const { return Key.GetMeshShader(); }
FORCEINLINE FRHIAmplificationShader* GetAmplificationShader() const { return Key.GetAmplificationShader(); }
FORCEINLINE FRHIPixelShader* GetPixelShader() const { return Key.GetPixelShader(); }
FORCEINLINE FRHIGeometryShader* GetGeometryShader() const { return Key.GetGeometryShader(); }
/**
* Get the RHI vertex declaration.
*/
FORCEINLINE FRHIVertexDeclaration* GetVertexDeclaration() const { return Key.GetVertexDeclaration(); }
protected:
FBoundShaderStateKey Key;
bool bAddedToSingleThreadedCache;
};
/**
* Searches for a cached bound shader state.
* @return If a bound shader state matching the parameters is cached, it is returned; otherwise NULL is returned.
*/
extern RHI_API FCachedBoundShaderStateLink* GetCachedBoundShaderState(
FRHIVertexDeclaration* VertexDeclaration,
FRHIVertexShader* VertexShader,
FRHIPixelShader* PixelShader,
FRHIGeometryShader* GeometryShader = nullptr,
FRHIMeshShader* MeshShader = nullptr,
FRHIAmplificationShader* AmplificationShader = nullptr
);
extern RHI_API void EmptyCachedBoundShaderStates();
class FCachedBoundShaderStateLink_Threadsafe : public FCachedBoundShaderStateLink
{
public:
/** Adds the bound shader state to the cache. */
FCachedBoundShaderStateLink_Threadsafe(
FRHIVertexDeclaration* VertexDeclaration,
FRHIVertexShader* VertexShader,
FRHIPixelShader* PixelShader,
FRHIBoundShaderState* InBoundShaderState
)
: FCachedBoundShaderStateLink(VertexDeclaration, VertexShader, PixelShader, InBoundShaderState, false)
{
}
/** Adds the bound shader state to the cache. */
FCachedBoundShaderStateLink_Threadsafe(
FRHIVertexDeclaration* VertexDeclaration,
FRHIVertexShader* VertexShader,
FRHIPixelShader* PixelShader,
FRHIGeometryShader* GeometryShader,
FRHIBoundShaderState* InBoundShaderState
)
: FCachedBoundShaderStateLink(VertexDeclaration, VertexShader, PixelShader, GeometryShader, InBoundShaderState, false)
{
}
/** Adds the bound shader state to the cache. */
FCachedBoundShaderStateLink_Threadsafe(
FRHIMeshShader* MeshShader,
FRHIAmplificationShader* AmplificationShader,
FRHIPixelShader* PixelShader,
FRHIBoundShaderState* InBoundShaderState
)
: FCachedBoundShaderStateLink(MeshShader, AmplificationShader, PixelShader, InBoundShaderState, false)
{
}
RHI_API void AddToCache();
RHI_API void RemoveFromCache();
};
/**
* Searches for a cached bound shader state. Threadsafe version.
* @return If a bound shader state matching the parameters is cached, it is returned; otherwise NULL is returned.
*/
extern RHI_API FBoundShaderStateRHIRef GetCachedBoundShaderState_Threadsafe(
FRHIVertexDeclaration* VertexDeclaration,
FRHIVertexShader* VertexShader,
FRHIPixelShader* PixelShader,
FRHIGeometryShader* GeometryShader = nullptr,
FRHIMeshShader* MeshShader = nullptr,
FRHIAmplificationShader* AmplificationShader = nullptr
);