From 43929d60b76375ea7663f7758d0ea42ae2c019dc Mon Sep 17 00:00:00 2001 From: MoLing14725 Date: Thu, 8 Oct 2026 16:36:01 +0800 Subject: [PATCH] Fix screenshot readback active render target restoration --- CHANGELOG.md | 3 ++ Editor/Tools/Builtins/ScreenshotFunctions.cs | 4 +- Tests/Editor/ScreenshotFunctionsTests.cs | 43 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f418f..72c7848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Fixed +- Screenshot readback preserves the caller's active render target before `Graphics.Blit` changes it. The temporary readback texture is unbound before release, avoiding `Releasing render texture that is set to be RenderTexture.active!` warnings and leaving the previous target intact. + ## [0.6.10] - 2026-10-04 ### Fixed diff --git a/Editor/Tools/Builtins/ScreenshotFunctions.cs b/Editor/Tools/Builtins/ScreenshotFunctions.cs index 7ea7b5c..13fb0ce 100644 --- a/Editor/Tools/Builtins/ScreenshotFunctions.cs +++ b/Editor/Tools/Builtins/ScreenshotFunctions.cs @@ -1283,7 +1283,8 @@ internal static void FlipTextureVertically(Texture2D texture) internal static Texture2D ReadTextureToTexture2D(Texture sourceTexture, int width, int height, bool flipVertically) { RenderTexture readableRenderTexture = null; - RenderTexture previousActive = null; + // Blit binds its destination, so preserve the caller's target before rendering. + RenderTexture previousActive = RenderTexture.active; Texture2D screenshot = null; try @@ -1292,7 +1293,6 @@ internal static Texture2D ReadTextureToTexture2D(Texture sourceTexture, int widt readableRenderTexture.Create(); Graphics.Blit(sourceTexture, readableRenderTexture); - previousActive = RenderTexture.active; RenderTexture.active = readableRenderTexture; screenshot = ReadActiveRenderTextureToTexture2D(width, height, flipVertically); diff --git a/Tests/Editor/ScreenshotFunctionsTests.cs b/Tests/Editor/ScreenshotFunctionsTests.cs index d9b898d..612527d 100644 --- a/Tests/Editor/ScreenshotFunctionsTests.cs +++ b/Tests/Editor/ScreenshotFunctionsTests.cs @@ -1,6 +1,7 @@ // Copyright (C) Funplay. Licensed under MIT. using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -113,6 +114,48 @@ public void ShouldFlipPlayModeViewRenderTexture_DefaultsToTrue() Assert.IsTrue(ScreenshotFunctions.ShouldFlipPlayModeViewRenderTexture()); } + [TestCase(false)] + [TestCase(true)] + public void ReadTextureToTexture2D_RestoresPreviousTargetWithoutReleasingAnActiveTexture(bool bindPrevious) + { + var originalActive = RenderTexture.active; + var previous = new RenderTexture(2, 2, 0, RenderTextureFormat.ARGB32); + var source = new Texture2D(2, 2, TextureFormat.RGBA32, false); + var warnings = new List(); + Texture2D screenshot = null; + Application.LogCallback onLog = (message, stack, type) => + { + if (type == LogType.Warning) + warnings.Add(message); + }; + + try + { + previous.Create(); + Fill(source, Color.red); + var expected = bindPrevious ? previous : null; + RenderTexture.active = expected; + Application.logMessageReceived += onLog; + + screenshot = ScreenshotFunctions.ReadTextureToTexture2D(source, 2, 2, flipVertically: false); + + Assert.AreEqual(expected, RenderTexture.active, "Screenshot readback must preserve the caller's active target."); + Assert.IsFalse(warnings.Any(message => message.Contains("Releasing render texture")), + "The temporary readback target must be unbound before release."); + AssertRed(screenshot.GetPixel(0, 0)); + } + finally + { + Application.logMessageReceived -= onLog; + RenderTexture.active = originalActive; + if (screenshot != null) + UnityEngine.Object.DestroyImmediate(screenshot); + previous.Release(); + UnityEngine.Object.DestroyImmediate(previous); + UnityEngine.Object.DestroyImmediate(source); + } + } + [Test] public void ShouldFlipCameraRenderTexture_DefaultsToFalse() {