mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-15 17:59:22 -05:00
* Fix leaky swapchain when resizing on game mode and dispose of handle after usage VideoRecorder would get a strong handle copy of the swapchain every frame and retain it, causing native to fail to shutdown the copies from it Made usage of getting native swapchain consistent on managed on other stuff, doing it like this ensures GC properly disposes of the strong handles even not disposing explicitly Remove now unused ScreenRecorder.def and ScreenshotService.def There are still optimizations to swapchain I'd like to send on another commit, game is allocating way more swapchains than needed even without the leak, has two completely different paths depending if you are MSAA or not, this can all be much simpler https://files.facepunch.com/sampavlovic/1b1811b1/EjDyxbTahs.png * Remove NativeLayerRenderTarget, was unused and fucked * Keep it as an ITexture/HRenderTextureStrong in managed so we avoid IDisposable, ReadTextureAsync with ITexture
84 lines
2.2 KiB
C#
84 lines
2.2 KiB
C#
using NativeEngine;
|
|
using System.Collections.Concurrent;
|
|
using System.IO;
|
|
|
|
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// Provides functionality to capture and save screenshots in various formats.
|
|
/// </summary>
|
|
internal static class ScreenshotService
|
|
{
|
|
[ConVar( "screenshot_prefix", Help = "Prefix for auto-generated screenshot filenames" )]
|
|
public static string ScreenshotPrefix { get; set; } = "sbox";
|
|
|
|
private record ScreenshotRequest( string FilePath );
|
|
|
|
private static readonly ConcurrentQueue<ScreenshotRequest> _pendingRequests = new();
|
|
|
|
/// <summary>
|
|
/// Captures the screen and saves it as a PNG file.
|
|
/// </summary>
|
|
internal static string RequestCapture()
|
|
{
|
|
string filePath = ScreenCaptureUtility.GenerateScreenshotFilename( "png" );
|
|
|
|
_pendingRequests.Enqueue( new ScreenshotRequest( filePath ) );
|
|
|
|
return filePath;
|
|
}
|
|
|
|
internal static void ProcessFrame( IRenderContext context, ITexture nativeTexture )
|
|
{
|
|
if ( nativeTexture.IsNull || !nativeTexture.IsStrongHandleValid() )
|
|
return;
|
|
|
|
while ( _pendingRequests.TryDequeue( out var request ) )
|
|
{
|
|
CaptureRenderTexture( context, nativeTexture, request.FilePath );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Captures the current render target and saves it to the specified file.
|
|
/// </summary>
|
|
private static void CaptureRenderTexture( IRenderContext context, ITexture nativeTexture, string filePath )
|
|
{
|
|
try
|
|
{
|
|
Bitmap bitmap = null;
|
|
|
|
context.ReadTextureAsync( nativeTexture, ( pData, format, mipLevel, width, height, _ ) =>
|
|
{
|
|
try
|
|
{
|
|
bitmap = new Bitmap( width, height );
|
|
|
|
pData.CopyTo( bitmap.GetBuffer() );
|
|
|
|
var rgbData = bitmap.ToFormat( ImageFormat.RGB888 );
|
|
Services.Screenshots.AddScreenshotToLibrary( rgbData, width, height );
|
|
|
|
var dir = Path.GetDirectoryName( filePath );
|
|
if ( dir != null )
|
|
{
|
|
Directory.CreateDirectory( dir );
|
|
}
|
|
var encodedBytes = bitmap.ToPng();
|
|
File.WriteAllBytes( filePath, encodedBytes );
|
|
}
|
|
catch ( Exception ex )
|
|
{
|
|
Log.Error( $"Error creating bitmap from texture: {ex.Message}" );
|
|
}
|
|
} );
|
|
|
|
Log.Info( $"Screenshot saved to: {filePath}" );
|
|
}
|
|
catch ( Exception ex )
|
|
{
|
|
Log.Error( $"Error capturing screenshot: {ex.Message}" );
|
|
}
|
|
}
|
|
}
|