mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-20 22:38:16 -04:00
* Remove unnecessary static singletons in MainMenu code * Empty SceneWorld delete queues during shutdown * Dresser cancel async load operation on destroy * Use reflection to null references to static native resources on shutdown This way we don't have to remember doing this manually. * Fix SoundOcclusionSystem using static lists to reference resources * Sound System: Use weak references to refer to scenes * Cleanup static logging listeners that kept strong refs to panels * UISystem Cleanup, make sure all panel/stylesheet refs are released * RenderTarget and RenderAttributes shutdown cleanup * Rework AvatarLoader, ThumbLoader & HTTPImageLoader Cache First try to go through ResourceLibrary.WeakIndex which might already hold the texture. If there is no hit, go through a second cache that caches HTTP & Steam API response bytes instead of textures. We want to cache the response bytes rather than the actual Texture, so stuff cached sits in RAM not VRAM. Before avatars and thumbs would reside in VRAM. * Fix rendertarget leak in CommandList.Attr.SetValue GetDepthTarget() / GetColorTarget() return a new strong handle (ref count +1). We need to DestroyStrongHandle() that ref. So handles don't leak. * Call EngineLoop.DrainFrameEndDisposables before shutdown * NativeResourceCache now report leaks on shutdown * Override Resource.Destroy for native resources, kill stronghandles * Deregister SceneWorld from SceneWorld.All during destruction * Ensure async texture loading cancels on shutdown * SkinnedModelRender bonemergetarget deregister from target OnDisabled * Clear shaderMaterials cache during shutdown * Refactor Shutdown code Mostly renaming methods from Clear() -> Shutdown() Adding separate GlobalContext.Shutdown function (more aggressive than GlobalContext.Reset). Clear some static input state. * Deregister surfaces from Surface.All in OnDestroy * RunAllStaticConstructors when loading a mount * Advanced managed resource leak tracker enabled via `resource_leak_tracking 1` Works by never pruning the WeakTable in NativeResourceCache. So we can check for all resources if they are still being held on to and log a callstack.
166 lines
6.6 KiB
C#
166 lines
6.6 KiB
C#
using NativeEngine;
|
|
using System.Threading;
|
|
|
|
namespace Sandbox;
|
|
|
|
public sealed partial class RenderTarget
|
|
{
|
|
static Lock _lock = new();
|
|
static List<RenderTarget> All = new();
|
|
|
|
/// <summary>
|
|
/// Get a temporary render target. You should dispose the returned handle when you're done to return the textures to the pool.
|
|
/// </summary>
|
|
/// <param name="width">Width of the render target you want.</param>
|
|
/// <param name="height">Height of the render target you want.</param>
|
|
/// <param name="colorFormat">The format for the color buffer. If set to default we'll use whatever the current pipeline is using.</param>
|
|
/// <param name="depthFormat">The format for the depth buffer.</param>
|
|
/// <param name="msaa">The number of msaa samples you'd like. Msaa render textures are a pain in the ass so you're probably gonna regret trying to use this.</param>
|
|
/// <param name="numMips">Number of mips you want in this texture. You probably don't want this unless you want to generate mips in a second pass.</param>
|
|
/// <param name="targetName">The optional name of the render target</param>
|
|
/// <returns>A RenderTarget that is ready to render to.</returns>
|
|
public static RenderTarget GetTemporary( int width, int height, ImageFormat colorFormat = ImageFormat.Default, ImageFormat depthFormat = ImageFormat.Default, MultisampleAmount msaa = MultisampleAmount.MultisampleNone, int numMips = 1, string targetName = "" )
|
|
{
|
|
const int maxSize = 1024 * 16;
|
|
int maxMips = (int)Math.Log2( Math.Max( width, height ) );
|
|
|
|
if ( width <= 0 ) throw new ArgumentException( $"width should be higher than 0 (was {width}x{height})" );
|
|
if ( width > maxSize ) throw new ArgumentException( $"width should be lower than ({maxSize})" );
|
|
if ( height <= 0 ) throw new ArgumentException( $"height should be higher than 0 (was {width}x{height})" );
|
|
if ( height > maxSize ) throw new ArgumentException( $"height should be higher than {maxSize}" );
|
|
if ( numMips <= 0 ) throw new ArgumentException( $"numMips should be higher than 0 (was {width}x{height}x{numMips})" );
|
|
if ( numMips > 1 && msaa != MultisampleAmount.MultisampleNone ) throw new ArgumentException( $"Texture cannot have both msaa and mips at same time" );
|
|
|
|
numMips = Math.Min( numMips, maxMips );
|
|
|
|
if ( colorFormat == ImageFormat.Default )
|
|
colorFormat = Graphics.IdealColorFormat;
|
|
|
|
if ( depthFormat == ImageFormat.Default )
|
|
depthFormat = g_pRenderDevice.IsUsing32BitDepthBuffer() ? ImageFormat.D32FS8 : ImageFormat.D24S8;
|
|
|
|
int hash = HashCode.Combine( width, height, colorFormat, depthFormat, numMips, msaa == MultisampleAmount.MultisampleScreen ? (MultisampleAmount)RenderService.GetMultisampleType() : msaa, targetName );
|
|
|
|
RenderTarget rt = null;
|
|
|
|
lock ( _lock )
|
|
{
|
|
rt = All.FirstOrDefault( x => !x.Loaned && x.CreationHash == hash );
|
|
|
|
if ( rt == null )
|
|
{
|
|
// Depth formats can't legally be UAV and they generally hate having mips.
|
|
// An R32 or similar is usually bound as a depth target if you want those. (e.g depth pyramid)
|
|
bool realDepthFormat = depthFormat.IsDepthFormat();
|
|
|
|
var size = new Vector2( width, height );
|
|
var rtColor = colorFormat == ImageFormat.None ? null : Texture.CreateRenderTarget().WithFormat( colorFormat ).WithMSAA( msaa ).WithSize( size ).WithMips( numMips ).WithUAVBinding().Create( $"__cache_color_{targetName}_{hash}" );
|
|
var rtDepth = depthFormat == ImageFormat.None ? null : Texture.CreateRenderTarget().WithFormat( depthFormat ).WithMSAA( msaa ).WithSize( size ).WithMips( realDepthFormat ? 1 : numMips ).WithUAVBinding( !realDepthFormat ).Create( $"__cache_depth_{targetName}_{hash}" );
|
|
|
|
rt = new RenderTarget()
|
|
{
|
|
Loaned = true,
|
|
CreationHash = hash,
|
|
ColorTarget = rtColor,
|
|
DepthTarget = rtDepth,
|
|
Width = width,
|
|
Height = height
|
|
};
|
|
|
|
All.Add( rt );
|
|
}
|
|
|
|
rt.Loaned = true;
|
|
rt.FramesSinceUsed = 0;
|
|
}
|
|
|
|
return rt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a temporary render target. You should dispose the returned handle when you're done to return the textures to the pool.
|
|
/// </summary>
|
|
/// <param name="sizeFactor">Divide the screen size by this factor. 2 would be half screen sized. 1 for full screen sized.</param>
|
|
/// <param name="colorFormat">The format for the color buffer. If null we'll choose the most appropriate for where you are in the pipeline.</param>
|
|
/// <param name="depthFormat">The format for the depth buffer.</param>
|
|
/// <param name="msaa">The number of msaa samples you'd like. Msaa render textures are a pain in the ass so you're probably gonna regret trying to use this.</param>
|
|
/// <param name="numMips">Number of mips you want in this texture. You probably don't want this unless you want to generate mips in a second pass.</param>
|
|
/// <param name="targetName">The optional name of the render target</param>
|
|
/// <returns>A RenderTarget that is ready to render to.</returns>
|
|
public static RenderTarget GetTemporary( int sizeFactor, ImageFormat colorFormat = ImageFormat.Default, ImageFormat depthFormat = ImageFormat.Default, MultisampleAmount msaa = MultisampleAmount.MultisampleNone, int numMips = 1, string targetName = "" )
|
|
{
|
|
if ( sizeFactor <= 0 )
|
|
throw new ArgumentException( "cannot be lower than 1", nameof( sizeFactor ) );
|
|
|
|
var ss = Graphics.Viewport.Size;
|
|
|
|
if ( ss.x < 8 || ss.y < 8 )
|
|
{
|
|
Log.Warning( $"Really small viewport {ss}, forcing to 8x8" );
|
|
ss = 8;
|
|
}
|
|
|
|
return GetTemporary( (int)(ss.x / sizeFactor), (int)(ss.y / sizeFactor), colorFormat, depthFormat, msaa, numMips, targetName );
|
|
}
|
|
|
|
internal void Return( RenderTarget source )
|
|
{
|
|
source.Loaned = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called at the end of the frame. At this point none of the render targets that were loaned out
|
|
/// should be being used, so we can put them all back in the pool.
|
|
/// </summary>
|
|
internal static void EndOfFrame()
|
|
{
|
|
lock ( _lock )
|
|
{
|
|
for ( int i = All.Count - 1; i >= 0; i-- )
|
|
{
|
|
All[i].FramesSinceUsed++;
|
|
All[i].Loaned = false;
|
|
|
|
if ( All[i].FramesSinceUsed < 8 ) continue;
|
|
|
|
All[i].Destroy();
|
|
All.RemoveAt( i );
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Flush all the render targets out. Useful to do when screen size changes.
|
|
/// </summary>
|
|
internal static void Flush()
|
|
{
|
|
lock ( _lock )
|
|
{
|
|
foreach ( var entry in All )
|
|
{
|
|
// Defer deletion
|
|
entry.Loaned = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Destroy all cached render targets immediately. Called during shutdown
|
|
/// to release native texture handles before the resource system tears down.
|
|
/// </summary>
|
|
internal static void Shutdown()
|
|
{
|
|
EndOfFrame();
|
|
|
|
lock ( _lock )
|
|
{
|
|
for ( int i = All.Count - 1; i >= 0; i-- )
|
|
{
|
|
All[i].Destroy();
|
|
}
|
|
|
|
All.Clear();
|
|
}
|
|
}
|
|
}
|