bSupportsManualVertexFetch

bSupportsManualVertexFetch

#Overview

name: bSupportsManualVertexFetch

The value of this variable can be defined or overridden in .ini config files. 13 .ini config files referencing this setting variable.

It is referenced in 39 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of bSupportsManualVertexFetch is to indicate whether a particular graphics hardware and API combination supports manual vertex fetch, which is a technique used in shader programming for more efficient vertex data handling.

Key points about bSupportsManualVertexFetch:

  1. It is part of the Unreal Engine’s RHI (Rendering Hardware Interface) subsystem, specifically within the DataDrivenShaderPlatformInfo class.

  2. This variable is used across various rendering-related modules, including the core Engine, Niagara, and HairStrands plugins.

  3. The value is typically set based on the capabilities of the current graphics hardware and API. It’s determined during engine initialization and remains constant for a given platform.

  4. When manual vertex fetch is supported (bSupportsManualVertexFetch is true), shaders can manually read vertex attributes from buffers, potentially allowing for more optimized vertex processing.

  5. This flag affects how vertex factories are set up and how vertex data is passed to shaders. For example, in many places in the code, there are conditional blocks that change behavior based on whether manual vertex fetch is supported.

  6. It’s particularly important for performance optimization on certain platforms, as it can reduce the overhead of fixed-function vertex attribute fetching.

  7. Developers should be aware of this flag when working on custom vertex factories or shader code, as it may require different implementation strategies depending on whether manual vertex fetch is supported.

  8. Best practices include:

    • Always check this flag before implementing manual vertex fetch in shaders.
    • Design vertex factories and shaders to work with both manual and automatic vertex fetch, using preprocessor directives if necessary.
    • Be aware that manual vertex fetch support can vary between different graphics APIs and hardware generations.
  9. This variable interacts with other rendering system variables, such as those related to vertex buffer layouts and shader resource views (SRVs).

  10. When implementing new rendering features or optimizing existing ones, developers should consider how manual vertex fetch support (or lack thereof) might impact their designs.

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/Android/DataDrivenPlatformInfo.ini:65, section: [ShaderPlatform OPENGL_ES3_1_ANDROID]

Location: <Workspace>/Engine/Config/Android/DataDrivenPlatformInfo.ini:85, section: [ShaderPlatform VULKAN_ES3_1_ANDROID]

Location: <Workspace>/Engine/Config/Android/DataDrivenPlatformInfo.ini:107, section: [ShaderPlatform VULKAN_SM5_ANDROID]

Location: <Workspace>/Engine/Config/IOS/DataDrivenPlatformInfo.ini:56, section: [ShaderPlatform METAL]

Location: <Workspace>/Engine/Config/IOS/DataDrivenPlatformInfo.ini:69, section: [ShaderPlatform METAL_MRT]

Location: <Workspace>/Engine/Config/IOS/DataDrivenPlatformInfo.ini:81, section: [ShaderPlatform METAL_SIM]

Location: <Workspace>/Engine/Config/Mac/DataDrivenPlatformInfo.ini:120, section: [ShaderPlatform METAL_MRT_MAC]

Location: <Workspace>/Engine/Config/Mac/DataDrivenPlatformInfo.ini:132, section: [ShaderPlatform METAL_MACES3_1]

Location: <Workspace>/Engine/Config/TVOS/DataDrivenPlatformInfo.ini:27, section: [ShaderPlatform METAL_MRT_TVOS]

Location: <Workspace>/Engine/Config/TVOS/DataDrivenPlatformInfo.ini:38, section: [ShaderPlatform METAL_TVOS]

Location: <Workspace>/Engine/Config/VulkanPC/DataDrivenPlatformInfo.ini:269, section: [ShaderPlatform VULKAN_PCES3_1]

Location: <Workspace>/Engine/Config/Windows/DataDrivenPlatformInfo.ini:157, section: [ShaderPlatform PCD3D_ES3_1]

Location: <Workspace>/Engine/Config/Windows/DataDrivenPlatformInfo.ini:178, section: [ShaderPlatform OPENGL_PCES3_1]

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraMeshRendererProperties.cpp:766

Scope (from outer to inner):

file
function     void UNiagaraMeshRendererProperties::CollectPSOPrecacheData

Source code excerpt:

{
	const FVertexFactoryType* VFType = GetVertexFactoryType();
	bool bSupportsManualVertexFetch = VFType->SupportsManualVertexFetch(GMaxRHIFeatureLevel);
		
	for (int32 MeshIndex = 0; MeshIndex < Meshes.Num(); ++MeshIndex)
	{
		INiagaraRenderableMeshInterface* RenderableMeshInterface = nullptr;
		UStaticMesh* StaticMesh = nullptr;
		NiagaraMeshRendererPropertiesInternal::ResolveRenderableMeshInternal(Meshes[MeshIndex], InEmitter, RenderableMeshInterface, StaticMesh);

#Loc: <Workspace>/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraMeshRendererProperties.cpp:786

Scope (from outer to inner):

file
function     void UNiagaraMeshRendererProperties::CollectPSOPrecacheData

Source code excerpt:

						FPSOPrecacheParams& PSOPrecacheParams = OutParams.AddDefaulted_GetRef();
						PSOPrecacheParams.MaterialInterface = MeshMaterial;
						if (!bSupportsManualVertexFetch)
						{
							// Assuming here that all LOD use same vertex decl
							int32 MeshLODIdx = StaticMesh->GetMinLODIdx();
							if (StaticMesh->GetRenderData()->LODResources.IsValidIndex(MeshLODIdx))
							{
								FStaticMeshDataType Data;
								FVertexDeclarationElementList Elements;
								FNiagaraRenderableStaticMesh::InitVertexFactoryComponents(StaticMesh->GetRenderData()->LODResources[MeshLODIdx].VertexBuffers, nullptr, Data);
								FNiagaraMeshVertexFactory::GetVertexElements(GMaxRHIFeatureLevel, bSupportsManualVertexFetch, Data, Elements);
								PSOPrecacheParams.VertexFactoryDataList.Add(FPSOPrecacheVertexFactoryData(VFType, Elements));
							}
						}
						else
						{
							PSOPrecacheParams.VertexFactoryDataList.Add(FPSOPrecacheVertexFactoryData(VFType));

#Loc: <Workspace>/Engine/Plugins/FX/Niagara/Source/NiagaraVertexFactories/Private/NiagaraMeshVertexFactory.cpp:70

Scope (from outer to inner):

file
function     void FNiagaraMeshVertexFactory::GetVertexElements

Source code excerpt:

IMPLEMENT_TYPE_LAYOUT(FNiagaraMeshVertexFactoryShaderParametersPS);

void FNiagaraMeshVertexFactory::GetVertexElements(ERHIFeatureLevel::Type FeatureLevel, bool bSupportsManualVertexFetch, FStaticMeshDataType& Data, FVertexDeclarationElementList& Elements, FVertexStreamList& InOutStreams)
{
	if (Data.PositionComponent.VertexBuffer != NULL)
	{
		Elements.Add(AccessStreamComponent(Data.PositionComponent, 0, InOutStreams));
	}

	if (!bSupportsManualVertexFetch)
	{
		// only tangent,normal are used by the stream. the binormal is derived in the shader
		uint8 TangentBasisAttributes[2] = { 1, 2 };
		for (int32 AxisIndex = 0; AxisIndex < 2; AxisIndex++)
		{
			if (Data.TangentBasisComponents[AxisIndex].VertexBuffer != NULL)

#Loc: <Workspace>/Engine/Plugins/FX/Niagara/Source/NiagaraVertexFactories/Private/NiagaraMeshVertexFactory.cpp:135

Scope (from outer to inner):

file
function     void FNiagaraMeshVertexFactory::InitRHI

Source code excerpt:

{
	check(HasValidFeatureLevel());
	const bool bSupportsManualVertexFetch = SupportsManualVertexFetch(GetFeatureLevel());

	FVertexDeclarationElementList Elements;
	GetVertexElements(GetFeatureLevel(), bSupportsManualVertexFetch, Data, Elements, Streams);

#if NIAGARA_ENABLE_GPU_SCENE_MESHES
	if (bAddPrimitiveIDElement)
	{
		// TODO: Support GPU Scene on mobile? Maybe only for CPU particles?
		AddPrimitiveIdStreamElement(EVertexInputStreamType::Default, Elements, 13, 0xFF);

#Loc: <Workspace>/Engine/Plugins/FX/Niagara/Source/NiagaraVertexFactories/Private/NiagaraMeshVertexFactory.cpp:210

Scope (from outer to inner):

file
function     void FNiagaraMeshVertexFactory::GetVertexElements

Source code excerpt:

}

void FNiagaraMeshVertexFactory::GetVertexElements(ERHIFeatureLevel::Type FeatureLevel, bool bSupportsManualVertexFetch, FStaticMeshDataType& Data, FVertexDeclarationElementList& Elements)
{
	FVertexStreamList InOutStreams;
	GetVertexElements(FeatureLevel, bSupportsManualVertexFetch, Data, Elements, InOutStreams);

#if NIAGARA_ENABLE_GPU_SCENE_MESHES
	if (UseGPUScene(GMaxRHIShaderPlatform, GMaxRHIFeatureLevel)
		&& !PlatformGPUSceneUsesUniformBufferView(GMaxRHIShaderPlatform))
	{
		Elements.Add(FVertexElement(InOutStreams.Num(), 0, VET_UInt, 13, sizeof(uint32), true));

#Loc: <Workspace>/Engine/Plugins/FX/Niagara/Source/NiagaraVertexFactories/Public/NiagaraMeshVertexFactory.h:153

Scope (from outer to inner):

file
class        class FNiagaraMeshVertexFactory : public FNiagaraVertexFactoryBase

Source code excerpt:

	*/
	static NIAGARAVERTEXFACTORIES_API void GetPSOPrecacheVertexFetchElements(EVertexInputStreamType VertexInputStreamType, FVertexDeclarationElementList& Elements);
	static NIAGARAVERTEXFACTORIES_API void GetVertexElements(ERHIFeatureLevel::Type FeatureLevel, bool bSupportsManualVertexFetch, FStaticMeshDataType& Data, FVertexDeclarationElementList& Elements);

	/**
	* An implementation of the interface used by TSynchronizedResource to update the resource with new data from the game thread.
	*/
	NIAGARAVERTEXFACTORIES_API void SetData(FRHICommandListBase& RHICmdList, const FStaticMeshDataType& InData);

#Loc: <Workspace>/Engine/Plugins/FX/Niagara/Source/NiagaraVertexFactories/Public/NiagaraMeshVertexFactory.h:212

Scope (from outer to inner):

file
class        class FNiagaraMeshVertexFactory : public FNiagaraVertexFactoryBase

Source code excerpt:

	}
protected:
	static NIAGARAVERTEXFACTORIES_API void GetVertexElements(ERHIFeatureLevel::Type FeatureLevel, bool bSupportsManualVertexFetch, FStaticMeshDataType& Data, FVertexDeclarationElementList& Elements, FVertexStreamList& InOutStreams);

protected:
	FStaticMeshDataType Data;
	bool bAddPrimitiveIDElement;

	/** Uniform buffer with mesh particle parameters. */

#Loc: <Workspace>/Engine/Plugins/Runtime/HairStrands/Source/HairStrandsCore/Private/HairCardsVertexFactory.cpp:95

Scope (from outer to inner):

file
function     FHairCardsUniformBuffer CreateHairCardsVFUniformBuffer

Source code excerpt:

	const uint32 LODIndex,
	EHairGeometryType GeometryType, 
	bool bSupportsManualVertexFetch)
{
	FHairCardsVertexFactoryUniformShaderParameters UniformParameters;

	if (GeometryType == EHairGeometryType::Cards)
	{
		const FHairGroupInstance::FCards::FLOD& LOD = Instance->Cards.LODs[LODIndex];

#Loc: <Workspace>/Engine/Plugins/Runtime/HairStrands/Source/HairStrandsCore/Private/HairCardsVertexFactory.cpp:163

Scope (from outer to inner):

file
function     FHairCardsUniformBuffer CreateHairCardsVFUniformBuffer

Source code excerpt:

	}

	if (!bSupportsManualVertexFetch)
	{
		UniformParameters.PositionBuffer         = GNullVertexBuffer.VertexBufferSRV;
		UniformParameters.PreviousPositionBuffer = GNullVertexBuffer.VertexBufferSRV;
		UniformParameters.NormalsBuffer          = GNullVertexBuffer.VertexBufferSRV;
		UniformParameters.UVsBuffer              = GNullVertexBuffer.VertexBufferSRV;
		UniformParameters.MaterialsBuffer        = GNullVertexBuffer.VertexBufferSRV;

#Loc: <Workspace>/Engine/Plugins/Runtime/HairStrands/Source/HairStrandsCore/Private/HairCardsVertexFactory.cpp:362

Scope (from outer to inner):

file
function     void FHairCardsVertexFactory::InitResources

Source code excerpt:

	AddPrimitiveIdStreamElement(EVertexInputStreamType::Default, Elements, HAIR_CARDS_VF_PRIMITIVEID_STREAM_INDEX /*AttributeIndex*/, HAIR_CARDS_VF_PRIMITIVEID_STREAM_INDEX /*AttributeIndex_Mobile*/);

	// Note this is a local version of the VF's bSupportsManualVertexFetch, which take into account the feature level
	// When manual fetch is not supported, buffers are bound through input assembly based on vertex declaration. 
	// A vertex declaraction only access FVertexBuffer buffers, so we create wrappers of pooled buffers
	const bool bManualFetch = SupportsManualVertexFetch(CurrentFeatureLevel);
	if (!bManualFetch)
	{
		if (Data.GeometryType == EHairGeometryType::Cards)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Components/StaticMeshComponent.h:728

Scope (from outer to inner):

file
class        class UStaticMeshComponent : public UMeshComponent

Source code excerpt:

	ENGINE_API virtual void CollectPSOPrecacheData(const FPSOPrecacheParams& BasePrecachePSOParams, FMaterialInterfacePSOPrecacheParamsList& OutParams) override;
	/** Shared implementation for all StaticMesh derived components */
	using GetPSOVertexElementsFn = TFunctionRef<void(const FStaticMeshLODResources& LODRenderData, int32 LODIndex, bool bSupportsManualVertexFetch, FVertexDeclarationElementList& Elements)>;
	ENGINE_API void CollectPSOPrecacheDataImpl(const FVertexFactoryType* VFType, const FPSOPrecacheParams& BasePrecachePSOParams, GetPSOVertexElementsFn GetVertexElements, FMaterialInterfacePSOPrecacheParamsList& OutParams) const;
		
	/** Whether the component type supports static lighting. */
	virtual bool SupportsStaticLighting() const override
	{
		return true;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/InstancedStaticMesh.h:281

Scope: file

Source code excerpt:

	 */
	static ENGINE_API void GetPSOPrecacheVertexFetchElements(EVertexInputStreamType VertexInputStreamType, FVertexDeclarationElementList& Elements);
	static ENGINE_API void GetVertexElements(ERHIFeatureLevel::Type FeatureLevel, EVertexInputStreamType InputStreamType, bool bSupportsManualVertexFetch, FDataType& Data, FInstancedStaticMeshDataType& InstanceData, FVertexDeclarationElementList& Elements);

	/**
	 * An implementation of the interface used by TSynchronizedResource to update the resource with new data from the game thread.
	 */
	void SetData(FRHICommandListBase& RHICmdList, const FDataType& InData, const FInstancedStaticMeshDataType* InInstanceData)
	{

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/InstancedStaticMesh.h:336

Scope: file

Source code excerpt:

	}
protected:
	static ENGINE_API void GetVertexElements(ERHIFeatureLevel::Type FeatureLevel, EVertexInputStreamType InputStreamType, bool bSupportsManualVertexFetch, FDataType& Data, FInstancedStaticMeshDataType& InstanceData, FVertexDeclarationElementList& Elements, FVertexStreamList& Streams);

private:
	FInstancedStaticMeshDataType InstanceData;

	TUniformBufferRef<FInstancedStaticMeshVertexFactoryUniformShaderParameters> UniformBuffer;
};

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/SplineMeshComponent.cpp:843

Scope (from outer to inner):

file
function     void USplineMeshComponent::CollectPSOPrecacheData
lambda-function

Source code excerpt:

	int32 LightMapCoordinateIndex = GetStaticMesh()->GetLightMapCoordinateIndex();

	auto SMC_GetElements = [LightMapCoordinateIndex](const FStaticMeshLODResources& LODRenderData, int32 LODIndex, bool bSupportsManualVertexFetch, FVertexDeclarationElementList& Elements)
	{
		// FIXME: This will miss when SM component overrides vertex colors and source StaticMesh does not have vertex colors
		constexpr bool bOverrideColorVertexBuffer = false;
		FLocalVertexFactory::FDataType Data;
		InitSplineMeshVertexFactoryComponents(LODRenderData.VertexBuffers, nullptr /*VertexFactory*/, LightMapCoordinateIndex, bOverrideColorVertexBuffer, Data);
		FLocalVertexFactory::GetVertexElements(GMaxRHIFeatureLevel, EVertexInputStreamType::Default, bSupportsManualVertexFetch, Data, Elements);
	};

	FPSOPrecacheParams SplineMeshPSOParams = BasePrecachePSOParams;
	SplineMeshPSOParams.bReverseCulling ^= (SplineParams.StartScale.X < 0) ^ (SplineParams.StartScale.Y < 0);

	if (ShouldCreateNaniteProxy())

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/StaticMeshComponent.cpp:1572

Scope (from outer to inner):

file
function     void UStaticMeshComponent::CollectPSOPrecacheDataImpl

Source code excerpt:

	ERHIFeatureLevel::Type FeatureLevel = World ? World->GetFeatureLevel() : GMaxRHIFeatureLevel;

	bool bSupportsManualVertexFetch = VFType->SupportsManualVertexFetch(GMaxRHIFeatureLevel);
	bool bAnySectionCastsShadows = false;
	int32 MeshMinLOD = GetStaticMesh()->GetMinLODIdx();

	FPSOPrecacheVertexFactoryDataPerMaterialIndexList VFTypesPerMaterialIndex;
	FStaticMeshLODResourcesArray& LODResources = GetStaticMesh()->GetRenderData()->LODResources;
	for (int32 LODIndex = MeshMinLOD; LODIndex < LODResources.Num(); ++LODIndex)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/StaticMeshComponent.cpp:1582

Scope (from outer to inner):

file
function     void UStaticMeshComponent::CollectPSOPrecacheDataImpl

Source code excerpt:

		FStaticMeshLODResources& LODRenderData = LODResources[LODIndex];
		FVertexDeclarationElementList VertexElements;
		if (!bSupportsManualVertexFetch)
		{
			GetVertexElements(LODRenderData, LODIndex, bSupportsManualVertexFetch, VertexElements);
		}

		for (FStaticMeshSection& RenderSection : LODRenderData.Sections)
		{
			bAnySectionCastsShadows |= RenderSection.bCastShadow;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/StaticMeshComponent.cpp:1600

Scope (from outer to inner):

file
function     void UStaticMeshComponent::CollectPSOPrecacheDataImpl

Source code excerpt:

			}

			if (bSupportsManualVertexFetch)
			{
				VFsPerMaterial->VertexFactoryDataList.AddUnique(FPSOPrecacheVertexFactoryData(VFType));
			}
			else
			{	
				VFsPerMaterial->VertexFactoryDataList.AddUnique(FPSOPrecacheVertexFactoryData(VFType, VertexElements));

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/StaticMeshComponent.cpp:1654

Scope (from outer to inner):

file
function     void UStaticMeshComponent::CollectPSOPrecacheData
lambda-function

Source code excerpt:

	int32 LightMapCoordinateIndex = StaticMesh->GetLightMapCoordinateIndex();

	auto SMC_GetElements = [LightMapCoordinateIndex, &LODData = this->LODData](const FStaticMeshLODResources& LODRenderData, int32 LODIndex, bool bSupportsManualVertexFetch, FVertexDeclarationElementList& Elements)
	{
		int32 NumTexCoords = (int32)LODRenderData.VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords();
		int32 LODLightMapCoordinateIndex = LightMapCoordinateIndex < NumTexCoords ? LightMapCoordinateIndex : NumTexCoords - 1;
		bool bOverrideColorVertexBuffer = LODIndex < LODData.Num() && LODData[LODIndex].OverrideVertexColors != nullptr;
		FLocalVertexFactory::FDataType Data;
		InitStaticMeshVertexFactoryComponents(LODRenderData.VertexBuffers, nullptr /*VertexFactory*/, LODLightMapCoordinateIndex, bOverrideColorVertexBuffer, Data);
		FLocalVertexFactory::GetVertexElements(GMaxRHIFeatureLevel, EVertexInputStreamType::Default, bSupportsManualVertexFetch, Data, Elements);
	};
	
	if (ShouldCreateNaniteProxy())
	{
		if (NaniteLegacyMaterialsSupported())
		{

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/InstancedStaticMesh.cpp:759

Scope (from outer to inner):

file
function     void FInstancedStaticMeshVertexFactory::GetVertexElements

Source code excerpt:

	ERHIFeatureLevel::Type FeatureLevel,
	EVertexInputStreamType InputStreamType,
	bool bSupportsManualVertexFetch,
	FDataType& Data,
	FInstancedStaticMeshDataType& InstanceData,
	FVertexDeclarationElementList& Elements)
{
	FVertexStreamList VertexStreams;
	GetVertexElements(FeatureLevel, InputStreamType, bSupportsManualVertexFetch, Data, InstanceData, Elements, VertexStreams);
}

void FInstancedStaticMeshVertexFactory::GetVertexElements(
	ERHIFeatureLevel::Type FeatureLevel, 
	EVertexInputStreamType InputStreamType, 
	bool bSupportsManualVertexFetch, 
	FDataType& Data, 
	FInstancedStaticMeshDataType& InstanceData,
	FVertexDeclarationElementList& Elements, 
	FVertexStreamList& Streams)
{
	if (Data.PositionComponent.VertexBuffer != NULL)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/InstancedStaticMesh.cpp:782

Scope (from outer to inner):

file
function     void FInstancedStaticMeshVertexFactory::GetVertexElements

Source code excerpt:

	}

	if (!bSupportsManualVertexFetch)
	{
		// only tangent,normal are used by the stream. the binormal is derived in the shader
		uint8 TangentBasisAttributes[2] = { 1, 2 };
		for (int32 AxisIndex = 0; AxisIndex < 2; AxisIndex++)
		{
			if (Data.TangentBasisComponents[AxisIndex].VertexBuffer != NULL)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/InstancedStaticMesh.cpp:5019

Scope (from outer to inner):

file
function     void UInstancedStaticMeshComponent::CollectPSOPrecacheData
lambda-function

Source code excerpt:

	int32 LightMapCoordinateIndex = GetStaticMesh()->GetLightMapCoordinateIndex();
	
	auto ISMC_GetElements = [LightMapCoordinateIndex, InstanceBuffer, this](const FStaticMeshLODResources& LODRenderData, int32 LODIndex, bool bSupportsManualVertexFetch, FVertexDeclarationElementList& Elements)
	{
		FInstancedStaticMeshDataType InstanceData;
		FInstancedStaticMeshVertexFactory::FDataType Data;
		const FColorVertexBuffer* ColorVertexBuffer = LODRenderData.bHasColorVertexData ? &(LODRenderData.VertexBuffers.ColorVertexBuffer) : nullptr;
		if (LODData.IsValidIndex(LODIndex) && LODData[LODIndex].OverrideVertexColors)
		{
			ColorVertexBuffer = LODData[LODIndex].OverrideVertexColors;
		}
		InitInstancedStaticMeshVertexFactoryComponents(LODRenderData.VertexBuffers, ColorVertexBuffer, InstanceBuffer, nullptr /*VertexFactory*/, LightMapCoordinateIndex, bSupportsManualVertexFetch, Data, InstanceData);
		FInstancedStaticMeshVertexFactory::GetVertexElements(GMaxRHIFeatureLevel, EVertexInputStreamType::Default, bSupportsManualVertexFetch, Data, InstanceData, Elements);
	};

	if (ShouldCreateNaniteProxy())
	{
		if (NaniteLegacyMaterialsSupported())
		{

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/LocalVertexFactory.cpp:359

Scope (from outer to inner):

file
function     void FLocalVertexFactory::GetVertexElements

Source code excerpt:

}

void FLocalVertexFactory::GetVertexElements(ERHIFeatureLevel::Type FeatureLevel, EVertexInputStreamType InputStreamType, bool bSupportsManualVertexFetch, FDataType& Data, FVertexDeclarationElementList& Elements)
{
	FVertexStreamList VertexStreams;
	int32 ColorStreamIndex;
	GetVertexElements(FeatureLevel, InputStreamType, bSupportsManualVertexFetch, Data, Elements, VertexStreams, ColorStreamIndex);

	if (UseGPUScene(GMaxRHIShaderPlatform, GMaxRHIFeatureLevel) 
		&& !PlatformGPUSceneUsesUniformBufferView(GMaxRHIShaderPlatform))
	{
		Elements.Add(FVertexElement(VertexStreams.Num(), 0, VET_UInt, 13, sizeof(uint32), true));
	}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/LocalVertexFactory.cpp:469

Scope (from outer to inner):

file
function     void FLocalVertexFactory::GetVertexElements

Source code excerpt:

	ERHIFeatureLevel::Type FeatureLevel, 
	EVertexInputStreamType InputStreamType,
	bool bSupportsManualVertexFetch,
	FDataType& Data, 
	FVertexDeclarationElementList& Elements, 
	FVertexStreamList& InOutStreams, 
	int32& OutColorStreamIndex)
{
	check(InputStreamType == EVertexInputStreamType::Default);

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/LocalVertexFactory.cpp:487

Scope (from outer to inner):

file
function     void FLocalVertexFactory::GetVertexElements

Source code excerpt:

	// The vertex factories are then used during mobile rendering and will cause PSO creation failure.
	// First need to fix invalid usage of these vertex factories before this can be enabled again. (UE-165187)
	if (!bSupportsManualVertexFetch)
#endif // WITH_EDITOR
	{
		// Only the tangent and normal are used by the stream; the bitangent is derived in the shader.
		uint8 TangentBasisAttributes[2] = { 1, 2 };
		for (int32 AxisIndex = 0; AxisIndex < 2; AxisIndex++)
		{

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/SkinnedAsset.cpp:214

Scope (from outer to inner):

file
function     FPSOPrecacheVertexFactoryDataPerMaterialIndexList USkinnedAsset::GetVertexFactoryTypesPerMaterialIndex

Source code excerpt:

				// Force static from GPU point of view
				const FVertexFactoryType* CPUSkinVFType = &FLocalVertexFactory::StaticType;
				bool bSupportsManualVertexFetch = CPUSkinVFType->SupportsManualVertexFetch(GMaxRHIFeatureLevel);
				if (!bSupportsManualVertexFetch)
				{
					FVertexDeclarationElementList VertexElements;
					bool bOverrideColorVertexBuffer = false;
					FLocalVertexFactory::FDataType Data;
					InitStaticMeshVertexFactoryComponents(LODRenderData.StaticVertexBuffers, nullptr /*VertexFactory*/, 0, bOverrideColorVertexBuffer, Data);
					FLocalVertexFactory::GetVertexElements(GMaxRHIFeatureLevel, EVertexInputStreamType::Default, bSupportsManualVertexFetch, Data, VertexElements);
					VFsPerMaterial->VertexFactoryDataList.AddUnique(FPSOPrecacheVertexFactoryData(CPUSkinVFType, VertexElements));
				}
				else
				{
					VFsPerMaterial->VertexFactoryDataList.AddUnique(FPSOPrecacheVertexFactoryData(CPUSkinVFType));
				}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/LocalVertexFactory.h:75

Scope: file

Source code excerpt:


	static ENGINE_API void GetPSOPrecacheVertexFetchElements(EVertexInputStreamType VertexInputStreamType, FVertexDeclarationElementList& Elements);
	static ENGINE_API void GetVertexElements(ERHIFeatureLevel::Type FeatureLevel, EVertexInputStreamType InputStreamType, bool bSupportsManualVertexFetch, FDataType& Data, FVertexDeclarationElementList& Elements);

	/**
	 * An implementation of the interface used by TSynchronizedResource to update the resource with new data from the game thread.
	 */
	ENGINE_API void SetData(FRHICommandListBase& RHICmdList, const FDataType& InData);

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/LocalVertexFactory.h:175

Scope: file

Source code excerpt:

		ERHIFeatureLevel::Type FeatureLevel, 
		EVertexInputStreamType InputStreamType, 
		bool bSupportsManualVertexFetch,
		FDataType& Data, 
		FVertexDeclarationElementList& Elements, 
		FVertexStreamList& InOutStreams, 
		int32& OutColorStreamIndex);

	FDataType Data;

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionSceneProxy.cpp:119

Scope (from outer to inner):

file
function     FGeometryCollectionSceneProxy::FGeometryCollectionSceneProxy

Source code excerpt:

	, MeshDescription(Component->GetRestCollection()->RenderData->MeshDescription)
	, VertexFactory(GetScene().GetFeatureLevel())
	, bSupportsManualVertexFetch(VertexFactory.SupportsManualVertexFetch(GetScene().GetFeatureLevel()))
	, bSupportsTripleBufferVertexUpload(GRHISupportsMapWriteNoOverwrite)
#if WITH_EDITOR
	, bShowBoneColors(Component->GetShowBoneColors())
	, bSuppressSelectionMaterial(Component->GetSuppressSelectionMaterial())
	, VertexFactoryDebugColor(GetScene().GetFeatureLevel())
#endif

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionSceneProxy.cpp:249

Scope (from outer to inner):

file
function     void FGeometryCollectionSceneProxy::SetupVertexFactory

Source code excerpt:

	FGeometryCollectionVertexFactory::FDataType Data;
	
	FPositionVertexBuffer const& PositionVB = bSupportsManualVertexFetch ? MeshResource.PositionVertexBuffer : SkinnedPositionVertexBuffer;
	PositionVB.BindPositionVertexBuffer(&GeometryCollectionVertexFactory, Data);

	MeshResource.StaticMeshVertexBuffer.BindTangentVertexBuffer(&GeometryCollectionVertexFactory, Data);
	MeshResource.StaticMeshVertexBuffer.BindPackedTexCoordVertexBuffer(&GeometryCollectionVertexFactory, Data);
	MeshResource.StaticMeshVertexBuffer.BindLightMapVertexBuffer(&GeometryCollectionVertexFactory, Data, 0);

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionSceneProxy.cpp:259

Scope (from outer to inner):

file
function     void FGeometryCollectionSceneProxy::SetupVertexFactory

Source code excerpt:

	ColorVB.BindColorVertexBuffer(&GeometryCollectionVertexFactory, Data);

	if (bSupportsManualVertexFetch)
	{
		Data.BoneMapSRV = MeshResource.BoneMapVertexBuffer.GetSRV();
		Data.BoneTransformSRV = TransformBuffers[CurrentTransformBufferIndex].VertexBufferSRV;
		Data.BonePrevTransformSRV = PrevTransformBuffers[CurrentTransformBufferIndex].VertexBufferSRV;
	}
	else

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionSceneProxy.cpp:287

Scope (from outer to inner):

file
function     void FGeometryCollectionSceneProxy::CreateRenderThreadResources

Source code excerpt:

void FGeometryCollectionSceneProxy::CreateRenderThreadResources(FRHICommandListBase& RHICmdList)
{
	if (bSupportsManualVertexFetch)
	{
		// Initialize transform buffers and upload rest transforms.
		TransformBuffers.AddDefaulted(1);
		PrevTransformBuffers.AddDefaulted(1);

		TransformBuffers[0].NumTransforms = NumTransforms;

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionSceneProxy.cpp:372

Scope (from outer to inner):

file
function     void FGeometryCollectionSceneProxy::DestroyRenderThreadResources

Source code excerpt:

void FGeometryCollectionSceneProxy::DestroyRenderThreadResources()
{
	if (bSupportsManualVertexFetch)
	{
		for (int32 i = 0; i < TransformBuffers.Num(); i++)
		{
			TransformBuffers[i].ReleaseResource();
			PrevTransformBuffers[i].ReleaseResource();
		}

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionSceneProxy.cpp:429

Scope (from outer to inner):

file
function     void FGeometryCollectionSceneProxy::SetDynamicData_RenderThread

Source code excerpt:

	TransformVertexBuffersContainsRestTransforms = !DynamicData->IsDynamic;
		
	if (bSupportsManualVertexFetch)
	{
		const bool bLocalGeometryCollectionTripleBufferUploads = (GGeometryCollectionTripleBufferUploads != 0) && bSupportsTripleBufferVertexUpload;

		if (bLocalGeometryCollectionTripleBufferUploads && TransformBuffers.Num() == 1)
		{
			TransformBuffers.AddDefaulted(2);

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionSceneProxy.h:223

Scope (from outer to inner):

file
class        class FGeometryCollectionSceneProxy final : public FPrimitiveSceneProxy

Source code excerpt:

	FGeometryCollectionVertexFactory VertexFactory;
	
	bool bSupportsManualVertexFetch;
	FPositionVertexBuffer SkinnedPositionVertexBuffer;

	int32 CurrentTransformBufferIndex = 0;
	bool TransformVertexBuffersContainsRestTransforms = true;
	bool bSupportsTripleBufferVertexUpload = false;
	bool bRenderResourcesCreated = false;

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Private/DataDrivenShaderPlatformInfo.cpp:144

Scope (from outer to inner):

file
function     void FGenericDataDrivenShaderPlatformInfo::SetDefaultValues

Source code excerpt:

	bSupportsAsyncPipelineCompilation = true;
	bSupportsVertexShaderSRVs = true; // Explicitly overriden to false for ES 3.1 platforms via DDPI ini
	bSupportsManualVertexFetch = true;
	bSupportsVolumeTextureAtomics = true;
	bSupportsClipDistance = true;
	bSupportsShaderPipelines = true;
	MaxSamplers = 16;
}

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Private/DataDrivenShaderPlatformInfo.cpp:275

Scope (from outer to inner):

file
function     void FGenericDataDrivenShaderPlatformInfo::ParseDataDrivenShaderInfo

Source code excerpt:

	GET_SECTION_BOOL_HELPER(bSupportsAsyncPipelineCompilation);
	GET_SECTION_BOOL_HELPER(bSupportsVertexShaderSRVs);
	GET_SECTION_BOOL_HELPER(bSupportsManualVertexFetch);
	GET_SECTION_BOOL_HELPER(bRequiresReverseCullingOnMobile);
	GET_SECTION_BOOL_HELPER(bOverrideFMaterial_NeedsGBufferEnabled);
	GET_SECTION_BOOL_HELPER(bSupportsFFTBloom);
	GET_SECTION_BOOL_HELPER(bSupportsVertexShaderLayer);
	GET_SECTION_BINDLESS_SUPPORT_HELPER(BindlessSupport);
	GET_SECTION_BOOL_HELPER(bSupportsVolumeTextureAtomics);

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Private/DataDrivenShaderPlatformInfo.cpp:473

Scope (from outer to inner):

file
function     void FGenericDataDrivenShaderPlatformInfo::UpdatePreviewPlatforms

Source code excerpt:

				PREVIEW_USE_RUNTIME_VALUE(bSupportsSceneDataCompressedTransforms);
				PREVIEW_USE_RUNTIME_VALUE(bSupportsVertexShaderSRVs);
				PREVIEW_USE_RUNTIME_VALUE(bSupportsManualVertexFetch);
				PREVIEW_USE_RUNTIME_VALUE(bSupportsRealTypes);
				PREVIEW_USE_RUNTIME_VALUE(bSupportsUniformBufferObjects);

				// Settings that will never be supported in preview
				PREVIEW_FORCE_DISABLE(bSupportsShaderRootConstants);
				PREVIEW_FORCE_DISABLE(bSupportsShaderBundleDispatch);

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Public/DataDrivenShaderPlatformInfo.h:98

Scope (from outer to inner):

file
class        class FGenericDataDrivenShaderPlatformInfo

Source code excerpt:

	uint32 bSupportsAsyncPipelineCompilation : 1;
	uint32 bSupportsVertexShaderSRVs : 1; // Whether SRVs can be bound to vertex shaders (may be independent from ManualVertexFetch)
	uint32 bSupportsManualVertexFetch : 1;
	uint32 bRequiresReverseCullingOnMobile : 1;
	uint32 bOverrideFMaterial_NeedsGBufferEnabled : 1;
	uint32 bSupportsFFTBloom : 1;
	uint32 bSupportsInlineRayTracing : 1;
	uint32 bSupportsRayTracingShaders : 1;
	uint32 bSupportsVertexShaderLayer : 1;

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Public/DataDrivenShaderPlatformInfo.h:671

Scope (from outer to inner):

file
class        class FGenericDataDrivenShaderPlatformInfo
function     static const bool GetSupportsManualVertexFetch

Source code excerpt:

	{
		check(IsValid(Platform));
		return Infos[Platform].bSupportsManualVertexFetch;
	}

	static FORCEINLINE_DEBUGGABLE const bool GetRequiresReverseCullingOnMobile(const FStaticShaderPlatform Platform)
	{
		check(IsValid(Platform));
		return Infos[Platform].bRequiresReverseCullingOnMobile;