diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/GameFacePixelHashObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/GameFacePixelHashObserver.cs index f271d825..eefbdfc5 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/GameFacePixelHashObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/GameFacePixelHashObserver.cs @@ -100,18 +100,17 @@ private static RenderTexture GetRenderTexture() private void UpdateGameFacePixelHash() { - if (_isActive) + // If we are running in -nographics mode, the async task below fails, causing an exception inside + // the AsyncGPUReadback.Request that is difficult to catch. This ensures that the image data + // is only read when we have graphics + if (_isActive && SystemInfo.graphicsDeviceType != GraphicsDeviceType.Null) { // have to re-get this every time because it changes on resolution and other screen changes that update the gameface render target var cohtmlViewTexture = GetRenderTexture(); if (cohtmlViewTexture != null) { var wasActive = Interlocked.CompareExchange(ref _requestInProgress, string.Empty, null); - - // If we are running in -nographics mode, the async task below fails, causing an exception inside - // the AsyncGPUReadback.Request that is difficult to catch. This ensures that the image data - // is only read when we have graphics - if (wasActive == null && SystemInfo.graphicsDeviceType != GraphicsDeviceType.Null) + if (wasActive == null) { var frame = Time.frameCount; AsyncGPUReadback.Request(cohtmlViewTexture, 0, request => diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenshotCapture.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenshotCapture.cs index 5ea615ff..23caca8a 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenshotCapture.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenshotCapture.cs @@ -290,88 +290,101 @@ private IEnumerator UpdateGPUData(int frame, Action<(byte[], int, int)?> onCompl } } } - - // wait for end of frame or graphics data will be incorrect - yield return new WaitForEndOfFrame(); - - // get this in there so we don't get duplicate requests out for the same frame - if (GPUReadbackRequests.TryAdd(frame, null)) + + // If we are running in -nographics mode, the async task fails, causing an exception inside + // the AsyncGPUReadback.Request that is difficult to catch. This ensures that the screenshot + // is only taken when we have graphics + if (SystemInfo.graphicsDeviceType != GraphicsDeviceType.Null) { - var screenWidth = Screen.width; - var screenHeight = Screen.height; - if (_screenShotTexture == null || _screenShotTexture.width != screenWidth || _screenShotTexture.height != screenHeight) + // wait for end of frame or graphics data will be incorrect + yield return new WaitForEndOfFrame(); + + // get this in there so we don't get duplicate requests out for the same frame + if (GPUReadbackRequests.TryAdd(frame, null)) { - if (_screenShotTexture != null) - { - Object.Destroy(_screenShotTexture); - } + var screenWidth = Screen.width; + var screenHeight = Screen.height; - if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) - { - // why Metal defaults to B8G8R8A8_SRGB and thus flips the colors.. who knows.. - // this setting stops it from spamming log errors - _screenShotTexture = new RenderTexture(screenWidth, screenHeight, 0, GraphicsFormat.R8G8B8A8_SRGB); - } - else + if (_screenShotTexture == null || _screenShotTexture.width != screenWidth || + _screenShotTexture.height != screenHeight) { - _screenShotTexture = new RenderTexture(screenWidth, screenHeight, 0); + if (_screenShotTexture != null) + { + Object.Destroy(_screenShotTexture); + } + + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) + { + // why Metal defaults to B8G8R8A8_SRGB and thus flips the colors.. who knows.. + // this setting stops it from spamming log errors + _screenShotTexture = new RenderTexture(screenWidth, screenHeight, 0, + GraphicsFormat.R8G8B8A8_SRGB); + } + else + { + _screenShotTexture = new RenderTexture(screenWidth, screenHeight, 0); + } } - } - var theGraphicsFormat = _screenShotTexture.graphicsFormat; - - // If we are running in -nographics mode, the async task fails, causing an exception inside - // the AsyncGPUReadback.Request that is difficult to catch. This ensures that the screenshot - // is only taken when we have graphics - if (SystemInfo.graphicsDeviceType != GraphicsDeviceType.Null) - { + var theGraphicsFormat = _screenShotTexture.graphicsFormat; + try { ScreenCapture.CaptureScreenshotIntoRenderTexture(_screenShotTexture); - var readbackRequest = AsyncGPUReadback.Request(_screenShotTexture, 0, GraphicsFormat.R8G8B8A8_SRGB, request => - { - if (!request.hasError) + var readbackRequest = AsyncGPUReadback.Request(_screenShotTexture, 0, + GraphicsFormat.R8G8B8A8_SRGB, request => { - var pixels = request.GetData(); - var copyBuffer = _copyBuffer.Value; // uses a threadlocal to avoid re-allocating this on every readback - if (SystemInfo.graphicsUVStartsAtTop) + if (!request.hasError) { - // the pixels from the GPU are upside down, we need to reverse this for it to be right side up - var halfHeight = screenHeight / 2; - for (var i = 0; i <= halfHeight; i++) + var pixels = request.GetData(); + var copyBuffer = + _copyBuffer + .Value; // uses a threadlocal to avoid re-allocating this on every readback + if (SystemInfo.graphicsUVStartsAtTop) { - // swap rows - // bottom row to buffer - NativeArray.Copy(pixels, i * screenWidth, copyBuffer, 0, screenWidth); - // top row to bottom - NativeArray.Copy(pixels, (screenHeight - i - 1) * screenWidth, pixels, i * screenWidth, screenWidth); - // buffer to top row - NativeArray.Copy(copyBuffer, 0, pixels, (screenHeight - i - 1) * screenWidth, screenWidth); - } - } //else.. we're fine - - var imageOutput = ImageConversion.EncodeNativeArrayToJPG(pixels, theGraphicsFormat, (uint)screenWidth, (uint)screenHeight); - - RGDebug.LogDebug($"ScreenshotCapture - Captured screenshot for frame # {frame}"); - AddFrame(frame, (imageOutput.ToArray(), screenWidth, screenHeight)); - } - else - { - RGDebug.LogWarning($"ScreenshotCapture - Error capturing screenshot for frame # {frame}"); - AddFrame(frame, null); - } + // the pixels from the GPU are upside down, we need to reverse this for it to be right side up + var halfHeight = screenHeight / 2; + for (var i = 0; i <= halfHeight; i++) + { + // swap rows + // bottom row to buffer + NativeArray.Copy(pixels, i * screenWidth, copyBuffer, 0, + screenWidth); + // top row to bottom + NativeArray.Copy(pixels, (screenHeight - i - 1) * screenWidth, + pixels, i * screenWidth, screenWidth); + // buffer to top row + NativeArray.Copy(copyBuffer, 0, pixels, + (screenHeight - i - 1) * screenWidth, screenWidth); + } + } //else.. we're fine + + var imageOutput = ImageConversion.EncodeNativeArrayToJPG(pixels, theGraphicsFormat, + (uint)screenWidth, (uint)screenHeight); + + RGDebug.LogDebug($"ScreenshotCapture - Captured screenshot for frame # {frame}"); + AddFrame(frame, (imageOutput.ToArray(), screenWidth, screenHeight)); + } + else + { + RGDebug.LogWarning( + $"ScreenshotCapture - Error capturing screenshot for frame # {frame}"); + AddFrame(frame, null); + } - HandleCompletedActionCallbacks(); - }); + HandleCompletedActionCallbacks(); + }); // update from null to the real request GPUReadbackRequests[frame] = readbackRequest; } catch (Exception e) { - RGDebug.LogWarning($"ScreenshotCapture - Exception starting to capture screenshot for frame # {frame} - {e.Message}"); + RGDebug.LogWarning( + $"ScreenshotCapture - Exception starting to capture screenshot for frame # {frame} - {e.Message}"); } + } } }