Many newer overlays use IRenderTextures that are sized to the rendered viewport. This was completely broken, because a single viewport can be rendered on multiple viewports in a single frame. The end result of this was that in the better case, constant render targets were allocated and freed, which is extremely inefficient. In the worse case, many of these overlays completely failed to Dispose() their render targets, leading to *extremely* swift VRAM OOMs. This fixes all the overlays to properly cache resources per viewport. This uses new engine functionality, so it requires engine master. This is still a pretty lousy way to do GPU resource management but, well, anything better needs a render graph, so...
61 lines
2.2 KiB
C#
61 lines
2.2 KiB
C#
using System.Numerics;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Shared.Enums;
|
|
|
|
namespace Content.Client.Light;
|
|
|
|
/// <summary>
|
|
/// This exists just to copy <see cref="BeforeLightTargetOverlay"/> to the light render target
|
|
/// </summary>
|
|
public sealed class AfterLightTargetOverlay : Overlay
|
|
{
|
|
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
|
|
|
[Dependency] private readonly IOverlayManager _overlay = default!;
|
|
|
|
public const int ContentZIndex = LightBlurOverlay.ContentZIndex + 1;
|
|
|
|
public AfterLightTargetOverlay()
|
|
{
|
|
IoCManager.InjectDependencies(this);
|
|
ZIndex = ContentZIndex;
|
|
}
|
|
|
|
protected override void Draw(in OverlayDrawArgs args)
|
|
{
|
|
var viewport = args.Viewport;
|
|
var worldHandle = args.WorldHandle;
|
|
|
|
if (viewport.Eye == null)
|
|
return;
|
|
|
|
var lightOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
|
var lightRes = lightOverlay.GetCachedForViewport(args.Viewport);
|
|
var bounds = args.WorldBounds;
|
|
|
|
// at 1-1 render scale it's mostly fine but at 4x4 it's way too fkn big
|
|
var lightScale = viewport.LightRenderTarget.Size / (Vector2) viewport.Size;
|
|
var newScale = viewport.RenderScale / (Vector2.One / lightScale);
|
|
|
|
var localMatrix =
|
|
viewport.LightRenderTarget.GetWorldToLocalMatrix(viewport.Eye, newScale);
|
|
var diff = (lightRes.EnlargedLightTarget.Size - viewport.LightRenderTarget.Size);
|
|
var halfDiff = diff / 2;
|
|
|
|
// Pixels -> Metres -> Half distance.
|
|
// If we're zoomed in need to enlarge the bounds further.
|
|
args.WorldHandle.RenderInRenderTarget(viewport.LightRenderTarget,
|
|
() =>
|
|
{
|
|
// We essentially need to draw the cropped version onto the lightrendertarget.
|
|
var subRegion = new UIBox2i(halfDiff.X,
|
|
halfDiff.Y,
|
|
viewport.LightRenderTarget.Size.X + halfDiff.X,
|
|
viewport.LightRenderTarget.Size.Y + halfDiff.Y);
|
|
|
|
worldHandle.SetTransform(localMatrix);
|
|
worldHandle.DrawTextureRectRegion(lightRes.EnlargedLightTarget.Texture, bounds, subRegion: subRegion);
|
|
}, Color.Transparent);
|
|
}
|
|
}
|