// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= ShaderBaseClasses.cpp: Shader base classes =============================================================================*/ #include "ShaderBaseClasses.h" #include "PostProcess/SceneRenderTargets.h" #include "RendererModule.h" #include "ScenePrivate.h" #include "ParameterCollection.h" #include "VT/VirtualTextureTest.h" #include "VT/VirtualTextureSpace.h" #include "VT/VirtualTextureSystem.h" IMPLEMENT_TYPE_LAYOUT(FMaterialShader); IMPLEMENT_TYPE_LAYOUT(FMeshMaterialShader); IMPLEMENT_TYPE_LAYOUT(FDebugUniformExpressionSet); /** If true, cached uniform expressions are allowed. */ int32 FMaterialShader::bAllowCachedUniformExpressions = true; /** Console variable ref to toggle cached uniform expressions. */ FAutoConsoleVariableRef FMaterialShader::CVarAllowCachedUniformExpressions( TEXT("r.AllowCachedUniformExpressions"), bAllowCachedUniformExpressions, TEXT("Allow uniform expressions to be cached."), ECVF_RenderThreadSafe); void FMeshMaterialShaderElementData::InitializeMeshMaterialData(const FSceneView* SceneView, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, const FMeshBatch& RESTRICT MeshBatch, int32 StaticMeshId, bool bAllowStencilDither) { FadeUniformBuffer = GDistanceCullFadedInUniformBuffer.GetUniformBufferRHI(); DitherUniformBuffer = GDitherFadedInUniformBuffer.GetUniformBufferRHI(); if (SceneView) { checkSlow(SceneView->bIsViewInfo); const FViewInfo* ViewInfo = (FViewInfo*)SceneView; if (StaticMeshId >= 0 && MeshBatch.bDitheredLODTransition && !(bAllowStencilDither && ViewInfo->bAllowStencilDither)) { if (ViewInfo->StaticMeshFadeOutDitheredLODMap[StaticMeshId]) { DitherUniformBuffer = ViewInfo->DitherFadeOutUniformBuffer; } else if (ViewInfo->StaticMeshFadeInDitheredLODMap[StaticMeshId]) { DitherUniformBuffer = ViewInfo->DitherFadeInUniformBuffer; } } if (PrimitiveSceneProxy) { int32 const PrimitiveIndex = PrimitiveSceneProxy->GetPrimitiveSceneInfo()->GetIndex(); if (ViewInfo->PrimitiveFadeUniformBufferMap.IsValidIndex(PrimitiveIndex) && ViewInfo->PrimitiveFadeUniformBufferMap[PrimitiveIndex]) { FadeUniformBuffer = ViewInfo->PrimitiveFadeUniformBuffers[PrimitiveIndex]; } } } } FName FMaterialShader::UniformBufferLayoutName(TEXT("Material")); FMaterialShader::FMaterialShader(const FMaterialShaderType::CompiledShaderInitializerType& Initializer) : FShader(Initializer) #if WITH_EDITORONLY_DATA , DebugUniformExpressionSet(Initializer.UniformExpressionSet) , DebugDescription(Initializer.DebugDescription) #endif // WITH_EDITORONLY_DATA { #if WITH_EDITORONLY_DATA check(!DebugDescription.IsEmpty()); DebugUniformExpressionUBLayout.CopyFrom(Initializer.UniformExpressionSet.GetUniformBufferLayoutInitializer()); #endif // WITH_EDITORONLY_DATA // Bind the material uniform buffer parameter. MaterialUniformBuffer.Bind(Initializer.ParameterMap,TEXT("Material")); for (int32 CollectionIndex = 0; CollectionIndex < Initializer.UniformExpressionSet.ParameterCollections.Num(); CollectionIndex++) { FShaderUniformBufferParameter CollectionParameter; CollectionParameter.Bind(Initializer.ParameterMap,*FString::Printf(TEXT("MaterialCollection%u"), CollectionIndex)); ParameterCollectionUniformBuffers.Add(CollectionParameter); } } FRHIUniformBuffer* FMaterialShader::GetParameterCollectionBuffer(const FGuid& Id, const FSceneInterface* SceneInterface) const { const FScene* Scene = (const FScene*)SceneInterface; FRHIUniformBuffer* UniformBuffer = Scene ? Scene->GetParameterCollectionBuffer(Id) : nullptr; if (!UniformBuffer) { FMaterialParameterCollectionInstanceResource** CollectionResource = GDefaultMaterialParameterCollectionInstances.Find(Id); if (CollectionResource && *CollectionResource) { UniformBuffer = (*CollectionResource)->GetUniformBuffer(); } } return UniformBuffer; } #if !(UE_BUILD_TEST || UE_BUILD_SHIPPING || !WITH_EDITOR) template static void DumpUB(const TLayout& Layout) { UE_LOG(LogShaders, Warning, TEXT("Layout %s, Hash %08x"), *Layout.GetDebugName(), Layout.GetHash()); FString ResourcesString; for (int32 Index = 0; Index < Layout.Resources.Num(); ++Index) { ResourcesString += FString::Printf(TEXT("%d "), (uint8)Layout.Resources[Index].MemberType); } UE_LOG(LogShaders, Warning, TEXT("Layout CB Size %d %d Resources: %s"), Layout.ConstantBufferSize, Layout.Resources.Num(), *ResourcesString); }; void FMaterialShader::VerifyExpressionAndShaderMaps(const FMaterialRenderProxy* MaterialRenderProxy, const FMaterial& Material, const FUniformExpressionCache* UniformExpressionCache) const { // Validate that the shader is being used for a material that matches the uniform expression set the shader was compiled for. FMaterialShaderMap* ShaderMap = Material.GetRenderingThreadShaderMap(); if (ShaderMap->GetShaderMapId().IsCookedId()) { // If this is a cooked shadermap, the debug uniform expression set won't be properly initialized...so skip the verification return; } const FUniformExpressionSet& MaterialUniformExpressionSet = ShaderMap->GetUniformExpressionSet(); bool bUniformExpressionSetMismatch = !DebugUniformExpressionSet.Matches(MaterialUniformExpressionSet) || UniformExpressionCache->CachedUniformExpressionShaderMap != ShaderMap; if (!bUniformExpressionSetMismatch) { if (UniformExpressionCache->LocalUniformBuffer.IsValid()) { if (UniformExpressionCache->LocalUniformBuffer.BypassUniform) { if (DebugUniformExpressionUBLayout.GetHash() != UniformExpressionCache->LocalUniformBuffer.BypassUniform->GetLayout().GetHash()) { UE_LOG(LogShaders, Warning, TEXT("Material Expression UB mismatch!")); DumpUB(DebugUniformExpressionUBLayout); DumpUB(UniformExpressionCache->LocalUniformBuffer.BypassUniform->GetLayout()); bUniformExpressionSetMismatch = true; } } else { if (DebugUniformExpressionUBLayout.GetHash() != UniformExpressionCache->LocalUniformBuffer.WorkArea->Layout->GetHash()) { UE_LOG(LogShaders, Warning, TEXT("Material Expression UB mismatch!")); DumpUB(DebugUniformExpressionUBLayout); DumpUB(*UniformExpressionCache->LocalUniformBuffer.WorkArea->Layout); bUniformExpressionSetMismatch = true; } } } else { if (DebugUniformExpressionUBLayout.GetHash() != UniformExpressionCache->UniformBuffer->GetLayout().GetHash()) { UE_LOG(LogShaders, Warning, TEXT("Material Expression UB mismatch!")); DumpUB(DebugUniformExpressionUBLayout); DumpUB(UniformExpressionCache->UniformBuffer->GetLayout()); bUniformExpressionSetMismatch = true; } } } if (bUniformExpressionSetMismatch) { const FShaderType* ShaderType = GetType(ShaderMap->GetPointerTable()); const FString ProxyName = *MaterialRenderProxy->GetFriendlyName(); const FString MaterialName = *Material.GetFriendlyName(); const TCHAR* ShaderMapDesc = ShaderMap->GetDebugDescription(); UE_LOG( LogShaders, Warning,//Fatal, TEXT("%s shader uniform expression set mismatch for material %s/%s.\n") TEXT("Shader compilation info: %s\n") TEXT("Material render proxy compilation info: %s\n") TEXT("Shader uniform expression set: %u preshaders, %u 2D textures, %u cube textures, %u array textures, %u 3D textures, %u virtual textures, shader map %p\n") TEXT("Material uniform expression set: %u preshaders, %u 2D textures, %u cube textures, %u array textures, %u 3D textures, %u virtual textures, shader map %p\n"), ShaderType->GetName(), *ProxyName, *MaterialName, *DebugDescription, ShaderMapDesc, DebugUniformExpressionSet.NumPreshaders, DebugUniformExpressionSet.NumTextureExpressions[(uint32)EMaterialTextureParameterType::Standard2D], DebugUniformExpressionSet.NumTextureExpressions[(uint32)EMaterialTextureParameterType::Cube], DebugUniformExpressionSet.NumTextureExpressions[(uint32)EMaterialTextureParameterType::Array2D], DebugUniformExpressionSet.NumTextureExpressions[(uint32)EMaterialTextureParameterType::Volume], DebugUniformExpressionSet.NumTextureExpressions[(uint32)EMaterialTextureParameterType::Virtual], UniformExpressionCache->CachedUniformExpressionShaderMap, MaterialUniformExpressionSet.UniformPreshaders.Num(), MaterialUniformExpressionSet.UniformTextureParameters[(uint32)EMaterialTextureParameterType::Standard2D].Num(), MaterialUniformExpressionSet.UniformTextureParameters[(uint32)EMaterialTextureParameterType::Cube].Num(), MaterialUniformExpressionSet.UniformTextureParameters[(uint32)EMaterialTextureParameterType::Array2D].Num(), MaterialUniformExpressionSet.UniformTextureParameters[(uint32)EMaterialTextureParameterType::Volume].Num(), MaterialUniformExpressionSet.UniformTextureParameters[(uint32)EMaterialTextureParameterType::Virtual].Num(), ShaderMap ); } } #endif template void FMaterialShader::SetParameters( TRHICommandList& RHICmdList, TRHIShader* ShaderRHI, const FMaterialRenderProxy* MaterialRenderProxy, const FMaterial& Material, const FSceneView& View) { const ERHIFeatureLevel::Type FeatureLevel = View.GetFeatureLevel(); FMaterialShaderMap* ShaderMap = Material.GetRenderingThreadShaderMap(); checkf(ShaderMap, TEXT("RenderingThreadShaderMap: %i"), ShaderMap ? 1 : 0); checkf(ShaderMap->IsValidForRendering(true) && Material.GetFeatureLevel() == FeatureLevel, TEXT("IsValid:%i, MaterialFeatureLevel:%i, FeatureLevel:%i"), ShaderMap->IsValidForRendering() ? 1 : 0, Material.GetFeatureLevel(), FeatureLevel); FUniformExpressionCache* UniformExpressionCache = &MaterialRenderProxy->UniformExpressionCache[FeatureLevel]; bool bUniformExpressionCacheNeedsDelete = false; //bool bForceExpressionEvaluation = false; #if !(UE_BUILD_TEST || UE_BUILD_SHIPPING || !WITH_EDITOR) if (bAllowCachedUniformExpressions) { // UE-46061 - Workaround for a rare crash with an outdated cached shader map if (UniformExpressionCache->CachedUniformExpressionShaderMap != ShaderMap) { UMaterialInterface* MtlInterface = Material.GetMaterialInterface(); UMaterialInterface* ProxyInterface = MaterialRenderProxy->GetMaterialInterface(); const FShaderType* ShaderType = GetType(ShaderMap->GetPointerTable()); ensureMsgf(false, TEXT("%s shader uniform expression set mismatched shader map for material %s/%s, forcing expression cache evaluation.\n") TEXT("Material: %s\n") TEXT("Proxy: %s\n"), ShaderType->GetName(), *MaterialRenderProxy->GetFriendlyName(), *Material.GetFriendlyName(), MtlInterface ? *MtlInterface->GetFullName() : TEXT("nullptr"), ProxyInterface ? *ProxyInterface->GetFullName() : TEXT("nullptr")); //bForceExpressionEvaluation = true; } } #endif if (!bAllowCachedUniformExpressions || UniformExpressionCache->CachedUniformExpressionShaderMap != ShaderMap) { FMaterialRenderContext MaterialRenderContext(MaterialRenderProxy, Material, &View); bUniformExpressionCacheNeedsDelete = true; UniformExpressionCache = new FUniformExpressionCache(); MaterialRenderProxy->EvaluateUniformExpressions(*UniformExpressionCache, MaterialRenderContext, &RHICmdList); SetLocalUniformBufferParameter(RHICmdList, ShaderRHI, MaterialUniformBuffer, UniformExpressionCache->LocalUniformBuffer); } else { SetUniformBufferParameter(RHICmdList, ShaderRHI, MaterialUniformBuffer, UniformExpressionCache->UniformBuffer); } #if !(UE_BUILD_TEST || UE_BUILD_SHIPPING || !WITH_EDITOR) VerifyExpressionAndShaderMaps(MaterialRenderProxy, Material, UniformExpressionCache); #endif { const TArray& ParameterCollections = UniformExpressionCache->ParameterCollections; const int32 ParameterCollectionsNum = ParameterCollections.Num(); // For shipping and test builds the assert above will be compiled out, but we're trying to verify that this condition is never hit. if (ParameterCollectionUniformBuffers.Num() < ParameterCollectionsNum) { UE_LOG(LogRenderer, Warning, TEXT("ParameterCollectionUniformBuffers.Num() [%u] < ParameterCollectionsNum [%u], this would crash below on SetUniformBufferParameter.\n") TEXT("RenderProxy=%s Material=%s"), ParameterCollectionUniformBuffers.Num(), ParameterCollectionsNum, *MaterialRenderProxy->GetFriendlyName(), *Material.GetFriendlyName() ); } check(ParameterCollectionUniformBuffers.Num() >= ParameterCollectionsNum); int32 NumToSet = FMath::Min(ParameterCollectionUniformBuffers.Num(), ParameterCollections.Num()); // Find each referenced parameter collection's uniform buffer in the scene and set the parameter for (int32 CollectionIndex = 0; CollectionIndex < NumToSet; CollectionIndex++) { FRHIUniformBuffer* UniformBuffer = GetParameterCollectionBuffer(ParameterCollections[CollectionIndex], View.Family->Scene); if (!UniformBuffer) { // Dump the currently registered parameter collections and the ID we failed to find. // In a cooked project these numbers are persistent so we can track back to the original // parameter collection that was being referenced and no longer exists FString InstancesString; TMultiMap::TIterator Iter = GDefaultMaterialParameterCollectionInstances.CreateIterator(); while (Iter) { FMaterialParameterCollectionInstanceResource* Instance = Iter.Value(); InstancesString += FString::Printf(TEXT("\n0x%p: %s: %s"), Instance, Instance ? *Instance->GetOwnerName().ToString() : TEXT("None"), *Iter.Key().ToString()); ++Iter; } UE_LOG(LogRenderer, Fatal, TEXT("Failed to find parameter collection buffer with GUID '%s'.\n") TEXT("Currently %i listed default instances: %s"), *ParameterCollections[CollectionIndex].ToString(), GDefaultMaterialParameterCollectionInstances.Num(), *InstancesString); } SetUniformBufferParameter(RHICmdList, ShaderRHI, ParameterCollectionUniformBuffers[CollectionIndex], UniformBuffer); } } if (bUniformExpressionCacheNeedsDelete) { delete UniformExpressionCache; } } // Doxygen struggles to parse these explicit specializations. Just ignore them for now. #if !UE_BUILD_DOCS #define IMPLEMENT_MATERIAL_SHADER_SetParameters( TRHICommandList, TRHIShader ) \ template RENDERER_API void FMaterialShader::SetParameters< TRHIShader, TRHICommandList >( \ TRHICommandList& RHICmdList, \ TRHIShader* ShaderRHI, \ const FMaterialRenderProxy* MaterialRenderProxy,\ const FMaterial& Material, \ const FSceneView& View \ ); IMPLEMENT_MATERIAL_SHADER_SetParameters(FRHICommandList, FRHIVertexShader); IMPLEMENT_MATERIAL_SHADER_SetParameters(FRHICommandList, FRHIGeometryShader); IMPLEMENT_MATERIAL_SHADER_SetParameters(FRHICommandList, FRHIPixelShader); IMPLEMENT_MATERIAL_SHADER_SetParameters(FRHICommandList, FRHIMeshShader); IMPLEMENT_MATERIAL_SHADER_SetParameters(FRHICommandList, FRHIComputeShader); IMPLEMENT_MATERIAL_SHADER_SetParameters(FRHIComputeCommandList, FRHIComputeShader); #endif void FMaterialShader::GetShaderBindings( const FScene* Scene, const FStaticFeatureLevel FeatureLevel, const FMaterialRenderProxy& MaterialRenderProxy, const FMaterial& Material, FMeshDrawSingleShaderBindings& ShaderBindings) const { check(Material.GetRenderingThreadShaderMap() && Material.GetRenderingThreadShaderMap()->IsValidForRendering() && Material.GetFeatureLevel() == FeatureLevel); const FUniformExpressionCache& UniformExpressionCache = MaterialRenderProxy.UniformExpressionCache[FeatureLevel]; checkf(UniformExpressionCache.CachedUniformExpressionShaderMap == Material.GetRenderingThreadShaderMap(), TEXT("UniformExpressionCache should be up to date, RenderProxy=%s Material=%s FeatureLevel=%d"), *MaterialRenderProxy.GetFriendlyName(), *Material.GetFriendlyName(), FeatureLevel); checkf(UniformExpressionCache.UniformBuffer, TEXT("NULL UniformBuffer, RenderProxy=%s Material=%s FeatureLevel=%d"), *MaterialRenderProxy.GetFriendlyName(), *Material.GetFriendlyName(), FeatureLevel); #if !(UE_BUILD_TEST || UE_BUILD_SHIPPING || !WITH_EDITOR) VerifyExpressionAndShaderMaps(&MaterialRenderProxy, Material, &UniformExpressionCache); #endif ShaderBindings.Add(MaterialUniformBuffer, UniformExpressionCache.UniformBuffer); { const TArray& ParameterCollections = UniformExpressionCache.ParameterCollections; const int32 ParameterCollectionsNum = ParameterCollections.Num(); // For shipping and test builds the assert above will be compiled out, but we're trying to verify that this condition is never hit. if (ParameterCollectionUniformBuffers.Num() < ParameterCollectionsNum) { UE_LOG(LogRenderer, Warning, TEXT("ParameterCollectionUniformBuffers.Num() [%u] < ParameterCollectionsNum [%u], this would crash below on SetUniformBufferParameter.\n") TEXT("RenderProxy=%s Material=%s"), ParameterCollectionUniformBuffers.Num(), ParameterCollectionsNum, *MaterialRenderProxy.GetFriendlyName(), *Material.GetFriendlyName() ); } check(ParameterCollectionUniformBuffers.Num() >= ParameterCollectionsNum); const int32 NumToSet = FMath::Min(ParameterCollectionUniformBuffers.Num(), ParameterCollections.Num()); // Find each referenced parameter collection's uniform buffer in the scene and set the parameter for (int32 CollectionIndex = 0; CollectionIndex < NumToSet; CollectionIndex++) { FRHIUniformBuffer* UniformBuffer = GetParameterCollectionBuffer(ParameterCollections[CollectionIndex], Scene); if (!UniformBuffer) { // Dump the currently registered parameter collections and the ID we failed to find. // In a cooked project these numbers are persistent so we can track back to the original // parameter collection that was being referenced and no longer exists FString InstancesString; TMultiMap::TIterator Iter = GDefaultMaterialParameterCollectionInstances.CreateIterator(); while (Iter) { FMaterialParameterCollectionInstanceResource* Instance = Iter.Value(); InstancesString += FString::Printf(TEXT("\n0x%p: %s: %s"), Instance, Instance ? *Instance->GetOwnerName().ToString() : TEXT("None"), *Iter.Key().ToString()); ++Iter; } UE_LOG(LogRenderer, Fatal, TEXT("Failed to find parameter collection buffer with GUID '%s'.\n") TEXT("Currently %i listed default instances: %s"), *ParameterCollections[CollectionIndex].ToString(), GDefaultMaterialParameterCollectionInstances.Num(), *InstancesString); } ShaderBindings.Add(ParameterCollectionUniformBuffers[CollectionIndex], UniformBuffer); } } } #if 0 bool FMaterialShader::Serialize(FArchive& Ar) { const bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); Ar << MaterialUniformBuffer; Ar << ParameterCollectionUniformBuffers; #if !ALLOW_SHADERMAP_DEBUG_DATA FDebugUniformExpressionSet DebugUniformExpressionSet; static FName DebugUniformExpressionUB(TEXT("DebugUniformExpressionUB")); FRHIUniformBufferLayout DebugUniformExpressionUBLayout(DebugUniformExpressionUB); FString DebugDescription; #endif Ar << DebugUniformExpressionSet; if (Ar.IsLoading()) { FName LayoutName; Ar << LayoutName; DebugUniformExpressionUBLayout = FRHIUniformBufferLayout(LayoutName); Ar << DebugUniformExpressionUBLayout.ConstantBufferSize; TArray ResourceOffsets; TArray ResourceTypes; Ar << ResourceOffsets; Ar << ResourceTypes; #if ALLOW_SHADERMAP_DEBUG_DATA DebugUniformExpressionUBLayout.Resources.Reserve(ResourceOffsets.Num()); for (int32 i = 0; i < ResourceOffsets.Num(); i++) { DebugUniformExpressionUBLayout.Resources.Emplace(FRHIUniformBufferResource{ ResourceOffsets[i], EUniformBufferBaseType(ResourceTypes[i]) }); } DebugUniformExpressionUBLayout.ComputeHash(); #endif } else { FName LayoutName = DebugUniformExpressionUBLayout.GetDebugName(); Ar << LayoutName; Ar << DebugUniformExpressionUBLayout.ConstantBufferSize; TArray ResourceOffsets; TArray ResourceTypes; ResourceOffsets.Reserve(DebugUniformExpressionUBLayout.Resources.Num()); ResourceTypes.Reserve(DebugUniformExpressionUBLayout.Resources.Num()); for (int32 i = 0; i < DebugUniformExpressionUBLayout.Resources.Num(); i++) { ResourceOffsets.Emplace(DebugUniformExpressionUBLayout.Resources[i].MemberOffset); ResourceTypes.Emplace(uint8(DebugUniformExpressionUBLayout.Resources[i].MemberType)); } Ar << ResourceOffsets; Ar << ResourceTypes; } Ar << DebugDescription; return bShaderHasOutdatedParameters; } #endif FMeshMaterialShader::FMeshMaterialShader(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer) : FMaterialShader(Initializer) { VertexFactoryParameters = Initializer.VertexFactoryType->CreateShaderParameters(Initializer.Target.GetFrequency(), Initializer.ParameterMap); } void FMeshMaterialShader::GetShaderBindings( const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FPrimitiveSceneProxy* PrimitiveSceneProxy, const FMaterialRenderProxy& MaterialRenderProxy, const FMaterial& Material, const FMeshPassProcessorRenderState& DrawRenderState, const FMeshMaterialShaderElementData& ShaderElementData, FMeshDrawSingleShaderBindings& ShaderBindings) const { FMaterialShader::GetShaderBindings(Scene, FeatureLevel, MaterialRenderProxy, Material, ShaderBindings); if (DrawRenderState.GetViewUniformBuffer()) { ShaderBindings.Add(GetUniformBufferParameter(), DrawRenderState.GetViewUniformBuffer()); } ShaderBindings.Add(GetUniformBufferParameter(), ShaderElementData.FadeUniformBuffer); ShaderBindings.Add(GetUniformBufferParameter(), ShaderElementData.DitherUniformBuffer); ShaderBindings.Add(GetUniformBufferParameter(), DrawRenderState.GetNaniteUniformBuffer()); } void FMeshMaterialShader::GetElementShaderBindings( const FShaderMapPointerTable& PointerTable, const FScene* Scene, const FSceneView* ViewIfDynamicMeshCommand, const FVertexFactory* VertexFactory, const EVertexInputStreamType InputStreamType, const FStaticFeatureLevel FeatureLevel, const FPrimitiveSceneProxy* PrimitiveSceneProxy, const FMeshBatch& MeshBatch, const FMeshBatchElement& BatchElement, const FMeshMaterialShaderElementData& ShaderElementData, FMeshDrawSingleShaderBindings& ShaderBindings, FVertexInputStreamArray& VertexStreams) const { const FVertexFactoryType* VertexFactoryType = GetVertexFactoryType(PointerTable); if (VertexFactoryType) { const FVertexFactoryShaderParameters* VFParameters = VertexFactoryParameters.Get(); if (VFParameters) { VertexFactoryType->GetShaderParameterElementShaderBindings(GetFrequency(), VFParameters, Scene, ViewIfDynamicMeshCommand, this, InputStreamType, FeatureLevel, VertexFactory, BatchElement, ShaderBindings, VertexStreams); } } if (UseGPUScene(GMaxRHIShaderPlatform, FeatureLevel) && VertexFactory->GetPrimitiveIdStreamIndex(FeatureLevel, InputStreamType) >= 0 && !(FeatureLevel == ERHIFeatureLevel::ES3_1 && GetFrequency() == SF_Vertex)) // Allow Primitive UB for VS on mobile { const FShaderType* ShaderType = GetType(PointerTable); ensureMsgf(!GetUniformBufferParameter().IsBound(), TEXT("Shader %s attempted to bind the Primitive uniform buffer even though Vertex Factory computes a PrimitiveId per-instance. This will break auto-instancing. Shaders should use GetPrimitiveData(PrimitiveId).Member instead of Primitive.Member."), ShaderType->GetName()); ensureMsgf(!BatchElement.PrimitiveUniformBuffer || (FeatureLevel == ERHIFeatureLevel::ES3_1), TEXT("FMeshBatchElement was assigned a PrimitiveUniformBuffer even though Vertex Factory %s fetches primitive shader data through a Scene buffer. The assigned PrimitiveUniformBuffer cannot be respected. Use PrimitiveUniformBufferResource instead for dynamic primitive data."), ShaderType->GetName()); } else { if (BatchElement.PrimitiveUniformBuffer) { ShaderBindings.Add(GetUniformBufferParameter(), BatchElement.PrimitiveUniformBuffer); } else { const FShaderType* ShaderType = GetType(PointerTable); checkf(BatchElement.PrimitiveUniformBufferResource, TEXT("%s expected a primitive uniform buffer but none was set on BatchElement.PrimitiveUniformBuffer or BatchElement.PrimitiveUniformBufferResource"), ShaderType->GetName()); ShaderBindings.Add(GetUniformBufferParameter(), BatchElement.PrimitiveUniformBufferResource->GetUniformBufferRHI()); } } } void FMeshMaterialShader::WriteFrozenVertexFactoryParameters(FMemoryImageWriter& Writer, const TMemoryImagePtr& InVertexFactoryParameters) const { const FVertexFactoryType* VertexFactoryType = GetVertexFactoryType(Writer.TryGetPrevPointerTable()); InVertexFactoryParameters.WriteMemoryImageWithDerivedType(Writer, VertexFactoryType->GetShaderParameterLayout(GetFrequency())); }