Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
## [3.0.1] - 2021-07-22
### Fixed
- Perform the appropriate color space conversion for Texture Sampling sources when required.
- Fix vertically flipped outputs on OpenGL hardware.
  • Loading branch information
Unity Technologies committed Jul 22, 2021
1 parent 959f5fc commit d36b1e5
Show file tree
Hide file tree
Showing 21 changed files with 386 additions and 210 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.0.1] - 2021-07-22
### Fixed
- Perform the appropriate color space conversion for Texture Sampling sources when required.
- Fix vertically flipped outputs on OpenGL hardware.

## [3.0.0] - 2021-06-17
### Changed
- Prevent invalid GPU callback data from being written to a frame: this change skips the problematic frame and logs an error message.
Expand Down
6 changes: 0 additions & 6 deletions Documentation~/KnownIssues.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ This page lists some known issues and limitations that you might experience with

**Workaround:** If you need to record a Beauty pass with TAA enabled on your recording camera, you should record it through its own recording session, separately from any other AOVs.

#### Images vertically flipped on OpenGL with Unity Editor 2021.2

**Known issue:** On OpenGL hardware, with Unity Editor 2021.2, the recorded output is vertically flipped for all input sources.

**Workaround:** In the Recorder properties, enable the **Flip Vertical** option, and then start a new recording. Note that this option is not available if you are using the Game View as the source of the recording. To solve this, you can still use a Targeted Camera as the source instead of the Game View.

<a name="360-view"></a>
#### 360 View recording issues and limitations

Expand Down
2 changes: 1 addition & 1 deletion Editor/Encoding/CoreMediaEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Unity.Media
{
internal class CoreMediaEncoderRegister : MediaEncoderRegister
{
internal override bool PerformsVerticalFlip => true; // the Core Media Encoder performs a VFlip in Unity Corethe encoder will flip its input vertically
internal override bool PerformsVerticalFlip => false;

internal sealed override VideoRecorderOutputFormat[] SupportedFormats { get; set; }

Expand Down
2 changes: 1 addition & 1 deletion Editor/ProRes/ProResEncoderPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ internal class ProResEncoderRegister : MediaEncoderRegister

internal sealed override VideoRecorderOutputFormat[] SupportedFormats { get; set; }

internal override bool PerformsVerticalFlip => false; // the ProRes wrappers do not perform a VFlip
internal override bool PerformsVerticalFlip => true;

internal override TextureFormat GetTextureFormat(MovieRecorderSettings settings)
{
Expand Down
5 changes: 5 additions & 0 deletions Editor/Sources/BaseRenderTextureInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public abstract class BaseRenderTextureInput : RecorderInput
/// </summary>
protected internal Texture2D ReadbackTexture { get; set; }

/// <summary>
/// Indicates whether or not the texture needs to be flipped vertically after being grabbed.
/// </summary>
internal bool? NeedToFlipVertically { get; set; }

/// <summary>
/// Stores the output image width.
/// </summary>
Expand Down
47 changes: 47 additions & 0 deletions Editor/Sources/Helpers/UnityHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.ComponentModel;
using System.IO;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEditor.Recorder.Input;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
Expand Down Expand Up @@ -225,5 +227,50 @@ internal static ImageRecorderSettings.ColorSpaceType GetColorSpaceType(GraphicsF
// All sRGB formats end with "_SRGB"?
return format.ToString().EndsWith("_SRGB") ? ImageRecorderSettings.ColorSpaceType.sRGB_sRGB : ImageRecorderSettings.ColorSpaceType.Unclamped_linear_sRGB;
}

/// <summary>
/// Returns the Recorder-specific color space matching the Unity color space.
/// </summary>
/// <param name="space">The Unity color space to probe</param>
/// <returns></returns>
/// <exception cref="InvalidEnumArgumentException">Throws an exception if the enum value is not as expected.</exception>
internal static ImageRecorderSettings.ColorSpaceType GetColorSpaceType(ColorSpace space)
{
switch (space)
{
case ColorSpace.Gamma:
return ImageRecorderSettings.ColorSpaceType.sRGB_sRGB;
case ColorSpace.Linear:
return ImageRecorderSettings.ColorSpaceType.Unclamped_linear_sRGB;
default:
throw new InvalidEnumArgumentException($"Unexpected color space '{space}'");
}
}

// Whether or not to perform a manual vertical flip, based on the user's intention as well as the characteristics
// of the current graphics API (OpenGL is vflipped compared to Metal & D3D) and capture source.
// wantFlippedTexture: If true, the user expects a vertically flipped texture.
// calledFrom: The object that calls this method.
internal static bool NeedToActuallyFlip(bool wantFlippedTexture, BaseRenderTextureInput calledFrom,
bool encoderAlreadyFlips)
{
// We need to take several things into account: what the user expects, whether or not the rendering is made
// on a GameView source, and whether or not the hardware is OpenGL.
bool isGameView = calledFrom is GameViewInput; // game view is already flipped
bool isCameraInputLegacyRP = calledFrom is CameraInput && UsingLegacyRP(); // legacy RP has vflipped camera input

// OpenGL causes a flipped image except if:
// * source is 360 camera
// * source is RenderTextureInput
// * source is RenderTextureSampler
// * source is CameraInput in a URP project
bool isFlippedBecauseOfOpenGL = !SystemInfo.graphicsUVStartsAtTop &&
!(calledFrom is Camera360Input || calledFrom is RenderTextureInput
|| calledFrom is RenderTextureSampler
|| (calledFrom is CameraInput && UsingURP()));
bool willBeFlipped = isGameView ^ encoderAlreadyFlips ^ isCameraInputLegacyRP ^ isFlippedBecauseOfOpenGL;

return willBeFlipped != wantFlippedTexture;
}
}
}
9 changes: 9 additions & 0 deletions Editor/Sources/RecorderSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,5 +336,14 @@ public virtual bool IsAccumulationSupported()
{
return false;
}

internal bool EncoderAlreadyFlips()
{
var movieRecorderSettings = this as MovieRecorderSettings;
bool encoderAlreadyFlips = false;
if (movieRecorderSettings != null)
encoderAlreadyFlips = movieRecorderSettings.encodersRegistered[movieRecorderSettings.encoderSelected].PerformsVerticalFlip;
return encoderAlreadyFlips;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,6 @@ internal override void SelfAdjustSettings()
{
cbis.RecordTransparency = CanCaptureAlpha() && CaptureAlpha;
}

var gis = input as GameViewInputSettings;
if (gis != null)
gis.FlipFinalOutput = SystemInfo.supportsAsyncGPUReadback;
}

[SerializeReference] AccumulationSettings _accumulationSettings = new AccumulationSettings();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,6 @@ internal override void SelfAdjustSettings()
}
}

var gis = selectedInput as GameViewInputSettings;
if (gis != null)
gis.FlipFinalOutput = SystemInfo.supportsAsyncGPUReadback;

m_ImageInputSelector.ForceEvenResolution(OutputFormat == VideoRecorderOutputFormat.MP4);
}
}
Expand Down
44 changes: 16 additions & 28 deletions Editor/Sources/Recorders/_Inputs/Camera/CameraInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ struct CanvasBackup

private InputStrategy m_InputStrategy;
private bool m_ModifiedResolution;
private TextureFlipper m_VFlipper;
private Camera m_UICamera;
private CanvasBackup[] m_CanvasBackups;

Expand All @@ -31,7 +30,7 @@ private abstract class InputStrategy
private Shader m_CopyShader;
private Material m_CopyMaterial;
private RenderTexture m_RenderTexture;
public bool flipVertically = true; // set to false to avoid vflip
protected internal bool NeedToFlipVertically;

public Camera targetCamera
{
Expand Down Expand Up @@ -106,13 +105,12 @@ private Material copyMaterial
get
{
if (m_CopyMaterial == null)
{
m_CopyMaterial = new Material(copyShader);
if (m_CaptureAlpha)
m_CopyMaterial.EnableKeyword("TRANSPARENCY_ON");
if (flipVertically)
m_CopyMaterial.EnableKeyword("VERTICAL_FLIP");
}

if (m_CaptureAlpha)
m_CopyMaterial.EnableKeyword("TRANSPARENCY_ON");
if (NeedToFlipVertically)
m_CopyMaterial.EnableKeyword("VERTICAL_FLIP");
return m_CopyMaterial;
}
}
Expand Down Expand Up @@ -202,14 +200,16 @@ protected Camera TargetCamera
/// <inheritdoc/>
protected internal override void BeginRecording(RecordingSession session)
{
if (cbSettings.FlipFinalOutput)
m_VFlipper = new TextureFlipper();
var encoderAlreadyFlips = session.settings.EncoderAlreadyFlips();
NeedToFlipVertically = UnityHelpers.NeedToActuallyFlip(cbSettings.FlipFinalOutput, this, encoderAlreadyFlips);

if (UnityHelpers.UsingLegacyRP())
m_InputStrategy = new CameraCommandBufferLegacyInputStrategy(cbSettings.RecordTransparency);
else
m_InputStrategy = new CaptureCallbackSRPInputStrategy(cbSettings.RecordTransparency);

m_InputStrategy.NeedToFlipVertically = NeedToFlipVertically.Value; // update the flag in the input strategy

switch (cbSettings.Source)
{
case ImageSource.ActiveCamera:
Expand Down Expand Up @@ -314,18 +314,6 @@ protected internal override void NewFrameStarting(RecordingSession session)
}

PrepFrameRenderTexture(session);
bool needToFlip = true;
var movieRecorderSettings = session.recorder.settings as MovieRecorderSettings;
if (movieRecorderSettings != null)
{
var encoderAlreadyFlips = movieRecorderSettings.encodersRegistered[movieRecorderSettings.encoderSelected].PerformsVerticalFlip;
needToFlip = encoderAlreadyFlips;
}

if (UnityHelpers.UsingLegacyRP())
m_InputStrategy.flipVertically = needToFlip; // regular pipeline
else
m_InputStrategy.flipVertically = !needToFlip; // scriptable render pipeline already flips input
m_InputStrategy.SetupCamera(OutputRenderTexture);
}

Expand Down Expand Up @@ -372,9 +360,6 @@ protected internal override void NewFrameReady(RecordingSession session)
m_CanvasBackups[i].canvas.worldCamera = m_CanvasBackups[i].camera;
}
}

if (cbSettings.FlipFinalOutput)
OutputRenderTexture = m_VFlipper.Flip(OutputRenderTexture);
}

/// <inheritdoc/>
Expand All @@ -392,9 +377,6 @@ protected override void Dispose(bool disposing)
if (GameViewSize.modifiedResolutionCount == 0)
GameViewSize.RestoreSize();
}

if (m_VFlipper != null)
m_VFlipper.Dispose();
}

base.Dispose(disposing);
Expand Down Expand Up @@ -427,5 +409,11 @@ void PrepFrameRenderTexture(RecordingSession session)
if (m_UICamera != null)
m_UICamera.targetTexture = OutputRenderTexture;
}

protected internal override void EndRecording(RecordingSession session)
{
base.EndRecording(session);
NeedToFlipVertically = null; // This variable is not valid anymore
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public string CameraTag
[SerializeField] private string cameraTag;

/// <summary>
/// Use this property if you need to vertically flip the final output.
/// Use this property if you want to apply a vertical flip to the final output.
/// </summary>
public bool FlipFinalOutput
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Shader "Hidden/Recorder/Inputs/MakeOpaque" {
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile ___ VERTICAL_FLIP

UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex);
uniform float4 _MainTex_ST;
Expand Down Expand Up @@ -42,7 +43,11 @@ Shader "Hidden/Recorder/Inputs/MakeOpaque" {
fixed4 frag (v2f i) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
fixed4 result = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.texcoord) * _Color;
float2 t = i.texcoord;
#if defined(VERTICAL_FLIP)
t.y = 1.0 - t.y;
#endif
fixed4 result = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, t) * _Color;
result.a = 1.0;
return result;
}
Expand Down
58 changes: 44 additions & 14 deletions Editor/Sources/Recorders/_Inputs/Camera360/Camera360Input.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace UnityEditor.Recorder.Input
class Camera360Input : BaseRenderTextureInput
{
bool m_ModifiedResolution;
TextureFlipper m_VFlipper = new TextureFlipper();

RenderTexture m_Cubemap1;
RenderTexture m_Cubemap2;
Expand All @@ -19,13 +18,43 @@ Camera360InputSettings settings360

Camera targetCamera { get; set; }

private Material copyMaterial
{
get
{
if (m_CopyMaterial == null)
m_CopyMaterial = new Material(copyShader);

if ((NeedToFlipVertically != null && NeedToFlipVertically.Value))
m_CopyMaterial.EnableKeyword("VERTICAL_FLIP");
return m_CopyMaterial;
}
}
private Material m_CopyMaterial;

private Shader copyShader
{
get
{
if (m_CopyShader == null)
m_CopyShader = Shader.Find("Hidden/Recorder/Inputs/CameraInput/Copy");
return m_CopyShader;
}
}
private Shader m_CopyShader;

RenderTexture m_TempCaptureTextureVFlip; // A temp RenderTexture for vertical flips

protected internal override void BeginRecording(RecordingSession session)
{
if (settings360.FlipFinalOutput)
m_VFlipper = new TextureFlipper();
var encoderAlreadyFlips = session.settings.EncoderAlreadyFlips();
NeedToFlipVertically = UnityHelpers.NeedToActuallyFlip(settings360.FlipFinalOutput, this, encoderAlreadyFlips);

OutputWidth = settings360.OutputWidth;
OutputHeight = settings360.OutputHeight;

if (NeedToFlipVertically.Value)
m_TempCaptureTextureVFlip = RenderTexture.GetTemporary(OutputWidth, OutputHeight);
}

protected internal override void NewFrameStarting(RecordingSession session)
Expand Down Expand Up @@ -93,23 +122,27 @@ protected internal override void NewFrameReady(RecordingSession session)
m_Cubemap1.ConvertToEquirect(OutputRenderTexture);
}

var movieRecorderSettings = session.settings as MovieRecorderSettings;
bool needToFlip = settings360.FlipFinalOutput; // whether or not the recorder settings have the flip box checked
if (movieRecorderSettings != null)
if (NeedToFlipVertically != null && NeedToFlipVertically.Value)
{
bool encoderAlreadyFlips = movieRecorderSettings.encodersRegistered[movieRecorderSettings.encoderSelected].PerformsVerticalFlip;
needToFlip = needToFlip ? encoderAlreadyFlips : !encoderAlreadyFlips;
var rememberActive = RenderTexture.active;
Graphics.Blit(OutputRenderTexture, m_TempCaptureTextureVFlip); // copy tex to rt
Graphics.Blit(m_TempCaptureTextureVFlip, OutputRenderTexture, copyMaterial); // copy rt to tex with vflip
RenderTexture.active = rememberActive; // restore active RT
}

if (needToFlip)
OutputRenderTexture = m_VFlipper.Flip(OutputRenderTexture);

targetCamera.stereoSeparation = eyesEyeSepBackup;
targetCamera.stereoTargetEye = eyeMaskBackup;

GL.sRGBWrite = sRGBWrite;
}

protected internal override void EndRecording(RecordingSession session)
{
base.EndRecording(session);
RenderTexture.ReleaseTemporary(m_TempCaptureTextureVFlip);
NeedToFlipVertically = null; // This variable is not valid anymore
}

protected override void Dispose(bool disposing)
{
if (disposing)
Expand All @@ -119,9 +152,6 @@ protected override void Dispose(bool disposing)

if (m_Cubemap2)
UnityHelpers.Destroy(m_Cubemap2);

if (m_VFlipper != null)
m_VFlipper.Dispose();
}

base.Dispose(disposing);
Expand Down
Loading

0 comments on commit d36b1e5

Please sign in to comment.