Deferred decals use unknown ref heuristic?

It looks like we have some textures used by decals that get put into the unknown ref heusritic. FDecalVisibilityViewPacket determines the sorted list of decals which are then rendered by FCompositionLighting::ProcessAfterBasePass() which tags the textures in the UB as being used, however they don’t go through static instance culling, so the texture streaming system puts them into unknown ref - so they need to stream in all their mips.

Are we somehow doing something wrong to cause this?

[Attachment Removed]

Steps to Reproduce
N/A

[Attachment Removed]

Hi there,

What you describe matches the expected UE behavior.

Deferred decals are treated as unknown reference textures because they bypass static mesh culling. They projected dynamically in screen space, the engine cannot predict pixel coverage.

As a result, the streaming manager conservatively assumes all mip levels may be needed, preventing LOD popping or missing textures. Placing decal textures in the Unknown Ref heuristic is UE’s standard way of handling dynamic, screen-space projected textures.

I hope this helps clarify the case. Please let me know if you have any further questions.

Best regards,

Henry Liu

[Attachment Removed]

Hi Henry,

Thanks for the reply. It make sense. Couldn’t UDecalComponent implement GetStreamingRenderAssetInfo() to just conservatively use the bounds of the decal projection space? I can certainly try an engine change to do that to see if it addresses our texture streaming concerns.

Edit: I see, UDecalComponent is not a UPrimitiveComponent so it has no way to do this.

[Attachment Removed]

Hi,

I think overriding GetStreamingRenderAssetInfo to return more conservative streaming data is a reasonable approach. It allows decals to participate in view-dependent texture streaming. Just keep in mind that you may need to deal with the right balance between CPU cost and memory usage based on your case. Also, take care with edge cases, such as very large decals viewed from a distance, very small but visible decals whose bounds may cause their streaming usage to be overestimated, or potential popping issues.

Cheers,

[Attachment Removed]

Henry,

I did a little more reading and because UDecalComponent is not derived from UPrimitiveComponent, the idea of overriding GetStreamingRenderAssetInfo() is not possible. After some more reading, I’m not sure I see a great way forward - FDynamicRenderAssetInstanceManager and FRenderAssetInstanceState expect to work on UPrimitiveComponent. This feels like a pretty substantial engine change to make this work for UDecalComponent. The other engine mod I considered was to make UDecalComponent a UPrimitiveComponent, however the way it’s scene proxy works isn’t compatible, which similarly seems like a substantial change.

Is there another technique you would recommend?

[Attachment Removed]

Hi,

The idea of override GetStreamingRenderAssetInfo is like this:

UCLASS(ClassGroup=(Rendering), meta=(BlueprintSpawnableComponent))
class MYPROJECT_API UMyDecalStreamingProxyComponent : public UPrimitiveComponent
{
    GENERATED_BODY()
public:
    UMyDecalStreamingProxyComponent();
    /** The decal component we mirror */
    UPROPERTY()
    class UDecalComponent* SourceDecal;
    void SetSourceDecal(UDecalComponent* InDecal);
 
    // UPrimitiveComponent interface
    virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
    virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override;
    virtual void GetStreamingRenderAssetInfo(
        FStreamingTextureLevelContext& LevelContext,
        TArray<FStreamingRenderAssetPrimitiveInfo>& OutStreamingRenderAssets
    ) const override;
};

Make it invisible, no collision, no render in main pass.

CreateSceneProxy returns nullptr, so the component never generates any renderable proxy.

CalcBounds mirrors the bounds of the associated UDecalComponent.

Feeds the mirrored bound with GetStreamingRenderAssetInfo.

Please let me know whether this class satisfies your use case, and feel free to reach out if you need any assistance with the implementation.

Cheers,

[Attachment Removed]

Right, I was trying to do this without adding a new component. Thanks for the suggestion.

The implementation was fairly straight forward, however I do need to create a scene proxy because inside FDynamicRenderAssetInstanceManager::IncrementalUpdate() it requires that the component have a scene proxy. Given that I’ve configured it to have no view relevance I don’t this would cause a problem.

[Attachment Removed]

That makes sense. Creating a minimal scene proxy with no view relevance is a reasonable approach.

Let me know if you have any follow-up questions.

Cheers,

[Attachment Removed]