Game.ScreenCaptureHelper
Assembly: Assembly-CSharp (Game)
Namespace: Game.UI
Type: public static class ScreenCaptureHelper
Base: System.Object
Summary:
Utility helpers for creating render targets, capturing screenshots of in-game cameras and reading GPU texture data asynchronously. Includes a nested AsyncRequest helper that wraps Unity's AsyncGPUReadback into a Task/NativeArray pattern suitable for Cities: Skylines 2 mod code. The helper is tailored to HDRP usage (reads pipeline settings from HDRenderPipelineAsset) and performs temporary state changes (disables UI overlay, toggles custom passes) while capturing to produce a clean screenshot and optional stylized composition.
Fields
-
private static ILog log
Provides a logger instance (LogManager.GetLogger("SceneFlow")). Used throughout the helper to emit debug/warn/error messages. -
private const string kOutlinesPassName
Constant string "Outlines Pass". Used to disable/enable the outlines custom pass during screenshot capture to avoid overlay artifacts.
Nested Types
public class AsyncRequest
Helper class that issues an AsyncGPUReadback request into a NativeArrayand exposes a Task to await completion. Useful to read a GPU texture (previewTexture) into CPU-accessible memory without blocking the render thread.
Summary / behavior:
- Allocates a NativeArray
Fields:
- private readonly TaskCompletionSource<bool> m_TaskCompletionSource
Task completion source used to signal request completion and result (success/failure).
private NativeArray<byte> m_Data
Native buffer that receives GPU readback data. Allocated with Allocator.Persistent — caller must ensure Dispose() is called to free it.
Properties:
- public int width { get; }
Width of the texture used for the request.
-
public int height { get; }
Height of the texture used for the request. -
public GraphicsFormat format { get; }
GraphicsFormat of the source texture. -
public ref NativeArray<byte> result => ref m_Data
Reference to the underlying NativeArray containing the readback data. Access only after the request completes. Be careful: the NativeArray is owned by the AsyncRequest; do not dispose it yourself if you plan to call Dispose() on the request.
Constructors:
- public AsyncRequest(Texture previewTexture)
Creates and issues an AsyncGPUReadback request for the provided previewTexture. Computes the required buffer size, allocates the NativeArray (Persistent), logs the issued request and requests into the native array. The request completion will call OnCompleted to fulfill the TaskCompletionSource.
Methods:
- private void OnCompleted(AsyncGPUReadbackRequest request)
Internal callback for the AsyncGPUReadback. Validates request dimensions and layerDataSize against the stored width/height/buffer. If sizes match, it waits for completion if not done (logs an error if that happens), logs errors for request.hasError, and sets the TaskCompletionSource result to true/false depending on match/success. If sizes do not match, sets result false and logs a warning.
-
public async Task Dispose()
Async disposal method: logs a debug message, awaits completion of the GPU readback (Complete()), then disposes the internal NativeArray to free persistent memory. Always call or await this to avoid native memory leaks. -
public Task Complete()
Returns the Task from the TaskCompletionSource so callers can await the GPU readback completion without disposing the buffer.
Properties
- (No top-level properties on ScreenCaptureHelper.)
Constructors
- (ScreenCaptureHelper is a static class — no public constructors.)
Methods
public static RenderTexture CreateRenderTarget(string name, int width, int height, GraphicsFormat format = GraphicsFormat.R8G8B8A8_UNorm)
Creates and returns a new RenderTexture with the specified name, dimensions and graphics format. The RenderTexture uses depth 0, is created with the provided GraphicsFormat, and is marked HideFlags.HideAndDontSave. The method calls Create() on the RenderTexture before returning it. Use this helper to allocate destination textures for screenshots or previews.
Notes: - Caller is responsible for destroying the returned RenderTexture when no longer needed (Object.Destroy). - The default GraphicsFormat is R8G8B8A8_UNorm; change only if you need different bit-depth/format.
public static void CaptureScreenshot(Camera camera, RenderTexture destination, MenuHelpers.SaveGamePreviewSettings settings)
Captures an image of the provided camera into the destination RenderTexture, applying optional stylization and overlay composition controlled by settings.
Behavior / steps: - No-ops if camera or destination is null. - Completes HDRPDotsInputs.punctualLightsJobHandle to ensure lighting jobs are finished before capture. - Queries the current HDRenderPipelineAsset platform settings to find the colorBufferFormat for matching HDRP render target format. - Creates a temporary HDRP-compatible RenderTexture (temporary render target) sized to destination and with a depth of 16, using the color buffer format. - Temporarily disables UI overlay and hides World rendering overlay: sets UIManager.defaultUISystem.enabled = false, existing RenderingSystem.hideOverlay = true, and disables the custom pass named "Outlines Pass" via CustomPassCache.SetPassEnabled. - Forces the camera to render into the temporary render target (camera.forceIntoRenderTexture = true; camera.targetTexture set), renders the camera 8 times (to stabilize frame-dependent effects), then restores camera.targetTexture and forceIntoRenderTexture and re-enables overlay/UI/custom pass. - Composes the temporary render target into the destination using a Material created from the "Hidden/ScreenCaptureCompose" shader: - If settings.stylized is true, enables keyword "STYLIZE" on the material and sets "_Radius" from settings.stylizedRadius. - If settings.overlayImage is set, loads the image asset (overlayImage.Load(0)) and assigns it to the material as "_Overlay", then un-loads it after blit. - Performs Graphics.Blit(tempRT, destination, material, 0). - Calls destination.IncrementUpdateCount() to notify the engine of the texture change. - Destroys the temporary material and temporary render texture.
Important notes & caveats: - This method modifies global/game systems (UIManager.defaultUISystem, RenderingSystem.hideOverlay, custom pass enable). It restores them on normal execution but if an exception occurs, caller code may need to ensure restoration — this method does not use try/finally in the current implementation. - The method relies on HDRenderPipelineAsset and HDRP-specific behavior. It expects the game to be running with HDRP; behavior on other pipelines is unspecified. - The method uses a hidden shader "Hidden/ScreenCaptureCompose" which must exist in the game's shader set for proper composition. The shader supports a stylize keyword and an overlay texture uniform. - overlayImage is a TextureAsset (Colossal asset wrapper). Load(0) is called to obtain the texture; the asset is unloaded after composition. - The loop renders the camera 8 times — this is likely to avoid frame-dependent or temporal effects. This may add cost; choose when to call accordingly.
Usage Example
// Simple capture into a new render target and composition settings
var destination = ScreenCaptureHelper.CreateRenderTarget("PreviewRT", 1024, 1024);
// Example settings type from the game's UI menu helpers
var settings = new MenuHelpers.SaveGamePreviewSettings {
stylized = true,
stylizedRadius = 1.5f,
overlayImage = myOverlayTextureAsset // TextureAsset or null
};
// Capture using a camera (e.g., main camera)
ScreenCaptureHelper.CaptureScreenshot(Camera.main, destination, settings);
// If you need CPU-side access to the captured texture pixels, create an AsyncRequest
// from a Texture that you can GPU-read (for example, a preview texture or a RenderTexture).
// Assuming 'previewTexture' is a Texture obtained from game rendering:
async Task<byte[]> ReadbackPreviewAsync(Texture previewTexture)
{
var request = new ScreenCaptureHelper.AsyncRequest(previewTexture);
bool success = await request.Complete(); // await completion
if (!success)
{
// handle failed readback
}
// Access native data (note: it's a NativeArray<byte>)
var native = request.result;
byte[] managed = new byte[native.Length];
native.CopyTo(managed); // copy to managed array if needed
await request.Dispose(); // free native memory
return managed;
}
Notes and recommendations - Always Dispose() AsyncRequest (or await Dispose()) to free the persistent NativeArray and avoid leaks. - Be cautious: CaptureScreenshot temporarily disables parts of the UI and rendering state. If your code can be interrupted (exceptions/early returns), ensure you guard state restoration externally or modify the helper to use try/finally. - The utility is intended for modding Cities: Skylines 2 and depends on game-specific classes (MenuHelpers.SaveGamePreviewSettings, TextureAsset, CustomPassCache, RenderingSystem, UIManager, HDRP types). Use only in that environment.