From 3bd357045f7581ee10d6c86ed8049bcebe35eda0 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 16 Feb 2022 19:15:39 -0300 Subject: [PATCH] Do not allow render targets not explicitly written by the fragment shader to be modified (#3063) * Do not allow render targets not explicitly written by the fragment shader to be modified * Shader cache version bump * Remove blank lines * Avoid redundant color mask updates * HostShaderCacheEntry can be null * Avoid more redundant glColorMask calls * nit: Mask -> Masks * Fix currentComponentMask * More efficient way to update _currentComponentMasks --- Ryujinx.Graphics.GAL/IRenderer.cs | 4 +- .../Programs/BinaryProgramRequest.cs | 8 ++- .../Programs/SourceProgramRequest.cs | 6 +- .../Multithreading/ThreadedRenderer.cs | 8 +-- Ryujinx.Graphics.GAL/ShaderInfo.cs | 12 ++++ .../Cache/Definition/HostShaderCacheEntry.cs | 6 +- .../Definition/HostShaderCacheEntryHeader.cs | 13 +++- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 41 +++++++++--- Ryujinx.Graphics.OpenGL/Pipeline.cs | 62 ++++++++++++++----- Ryujinx.Graphics.OpenGL/Program.cs | 23 ++++--- Ryujinx.Graphics.OpenGL/Renderer.cs | 8 +-- Ryujinx.Graphics.OpenGL/Shader.cs | 2 + Ryujinx.Graphics.Shader/ShaderProgramInfo.cs | 21 ++++--- .../Translation/EmitterContext.cs | 8 +-- .../Translation/ShaderConfig.cs | 21 ++----- .../Translation/ShaderHeader.cs | 61 +++--------------- .../Translation/Translator.cs | 3 +- 17 files changed, 176 insertions(+), 131 deletions(-) create mode 100644 Ryujinx.Graphics.GAL/ShaderInfo.cs diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs index a11932082..a36d999d6 100644 --- a/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/Ryujinx.Graphics.GAL/IRenderer.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.GAL BufferHandle CreateBuffer(int size); - IProgram CreateProgram(IShader[] shaders); + IProgram CreateProgram(IShader[] shaders, ShaderInfo info); ISampler CreateSampler(SamplerCreateInfo info); ITexture CreateTexture(TextureCreateInfo info, float scale); @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.GAL Capabilities GetCapabilities(); - IProgram LoadProgramBinary(byte[] programBinary); + IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info); void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data); diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs index 96bfedf8d..b4c6853f2 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs @@ -5,17 +5,21 @@ public ThreadedProgram Threaded { get; set; } private byte[] _data; + private bool _hasFragmentShader; + private ShaderInfo _info; - public BinaryProgramRequest(ThreadedProgram program, byte[] data) + public BinaryProgramRequest(ThreadedProgram program, byte[] data, bool hasFragmentShader, ShaderInfo info) { Threaded = program; _data = data; + _hasFragmentShader = hasFragmentShader; + _info = info; } public IProgram Create(IRenderer renderer) { - return renderer.LoadProgramBinary(_data); + return renderer.LoadProgramBinary(_data, _hasFragmentShader, _info); } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs index 8e4cd1d49..d808fe221 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs @@ -7,12 +7,14 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs public ThreadedProgram Threaded { get; set; } private IShader[] _shaders; + private ShaderInfo _info; - public SourceProgramRequest(ThreadedProgram program, IShader[] shaders) + public SourceProgramRequest(ThreadedProgram program, IShader[] shaders, ShaderInfo info) { Threaded = program; _shaders = shaders; + _info = info; } public IProgram Create(IRenderer renderer) @@ -24,7 +26,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs return threaded?.Base; }).ToArray(); - return renderer.CreateProgram(shaders); + return renderer.CreateProgram(shaders, _info); } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index df46b4289..5030fee62 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -268,10 +268,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading return handle; } - public IProgram CreateProgram(IShader[] shaders) + public IProgram CreateProgram(IShader[] shaders, ShaderInfo info) { var program = new ThreadedProgram(this); - SourceProgramRequest request = new SourceProgramRequest(program, shaders); + SourceProgramRequest request = new SourceProgramRequest(program, shaders, info); Programs.Add(request); New().Set(Ref((IProgramRequest)request)); @@ -355,11 +355,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading _baseRenderer.Initialize(logLevel); } - public IProgram LoadProgramBinary(byte[] programBinary) + public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) { var program = new ThreadedProgram(this); - BinaryProgramRequest request = new BinaryProgramRequest(program, programBinary); + BinaryProgramRequest request = new BinaryProgramRequest(program, programBinary, hasFragmentShader, info); Programs.Add(request); New().Set(Ref((IProgramRequest)request)); diff --git a/Ryujinx.Graphics.GAL/ShaderInfo.cs b/Ryujinx.Graphics.GAL/ShaderInfo.cs new file mode 100644 index 000000000..0c187e066 --- /dev/null +++ b/Ryujinx.Graphics.GAL/ShaderInfo.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL +{ + public struct ShaderInfo + { + public int FragmentOutputMap { get; } + + public ShaderInfo(int fragmentOutputMap) + { + FragmentOutputMap = fragmentOutputMap; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs index 68f6b3c1d..819c6bcc9 100644 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs +++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs @@ -77,7 +77,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition programInfo.Images.Count, programInfo.UsesInstanceId, programInfo.UsesRtLayer, - programInfo.ClipDistancesWritten); + programInfo.ClipDistancesWritten, + programInfo.FragmentOutputMap); CBuffers = programInfo.CBuffers.ToArray(); SBuffers = programInfo.SBuffers.ToArray(); Textures = programInfo.Textures.ToArray(); @@ -97,7 +98,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition Images, Header.UseFlags.HasFlag(UseFlags.InstanceId), Header.UseFlags.HasFlag(UseFlags.RtLayer), - Header.ClipDistancesWritten); + Header.ClipDistancesWritten, + Header.FragmentOutputMap); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs index 4b8b15bc2..c3c0de22f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs +++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition /// /// Host shader entry header used for binding information. /// - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x14)] + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)] struct HostShaderCacheEntryHeader { /// @@ -70,6 +70,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition /// public byte Reserved; + /// + /// Mask of components written by the fragment shader stage. + /// + public int FragmentOutputMap; + /// /// Create a new host shader cache entry header. /// @@ -78,6 +83,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition /// Count of texture descriptors /// Count of image descriptors /// Set to true if the shader uses instance id + /// Mask of clip distances that are written to on the shader + /// Mask of components written by the fragment shader stage public HostShaderCacheEntryHeader( int cBuffersCount, int sBuffersCount, @@ -85,13 +92,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition int imagesCount, bool usesInstanceId, bool usesRtLayer, - byte clipDistancesWritten) : this() + byte clipDistancesWritten, + int fragmentOutputMap) : this() { CBuffersCount = cBuffersCount; SBuffersCount = sBuffersCount; TexturesCount = texturesCount; ImagesCount = imagesCount; ClipDistancesWritten = clipDistancesWritten; + FragmentOutputMap = fragmentOutputMap; InUse = true; UseFlags = usesInstanceId ? UseFlags.InstanceId : UseFlags.None; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index f1e9f383d..bf76d592f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 3106; + private const ulong ShaderCodeGenVersion = 3063; // Progress reporting helpers private volatile int _shaderCount; @@ -188,7 +188,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan hostProgramBinarySpan); hostProgramBinary = hostProgramBinarySpan.ToArray(); - hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); + hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary, false, new ShaderInfo(-1)); } ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); @@ -252,7 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Shader // Compile shader and create program as the shader program binary got invalidated. shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, program.Code); - hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }); + hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, new ShaderInfo(-1)); task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => { @@ -303,7 +303,18 @@ namespace Ryujinx.Graphics.Gpu.Shader { hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan hostProgramBinarySpan); hostProgramBinary = hostProgramBinarySpan.ToArray(); - hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); + + bool hasFragmentShader = false; + int fragmentOutputMap = -1; + int fragmentIndex = (int)ShaderStage.Fragment - 1; + + if (hostShaderEntries[fragmentIndex] != null && hostShaderEntries[fragmentIndex].Header.InUse) + { + hasFragmentShader = true; + fragmentOutputMap = hostShaderEntries[fragmentIndex].Header.FragmentOutputMap; + } + + hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary, hasFragmentShader, new ShaderInfo(fragmentOutputMap)); } ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); @@ -426,7 +437,15 @@ namespace Ryujinx.Graphics.Gpu.Shader hostShaders.Add(hostShader); } - hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray()); + int fragmentIndex = (int)ShaderStage.Fragment - 1; + int fragmentOutputMap = -1; + + if (shaders[fragmentIndex] != null) + { + fragmentOutputMap = shaders[fragmentIndex].Info.FragmentOutputMap; + } + + hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), new ShaderInfo(fragmentOutputMap)); task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => { @@ -617,7 +636,7 @@ namespace Ryujinx.Graphics.Gpu.Shader shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code); - IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }); + IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, new ShaderInfo(-1)); cpShader = new ShaderBundle(hostProgram, shader); @@ -755,7 +774,15 @@ namespace Ryujinx.Graphics.Gpu.Shader hostShaders.Add(hostShader); } - IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray()); + int fragmentIndex = (int)ShaderStage.Fragment - 1; + int fragmentOutputMap = -1; + + if (shaders[fragmentIndex] != null) + { + fragmentOutputMap = shaders[fragmentIndex].Info.FragmentOutputMap; + } + + IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), new ShaderInfo(fragmentOutputMap)); gpShaders = new ShaderBundle(hostProgram, shaders); diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index ff5af42d1..c20ce8a32 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -53,7 +53,9 @@ namespace Ryujinx.Graphics.OpenGL private ClipOrigin _clipOrigin; private ClipDepthMode _clipDepthMode; - private readonly uint[] _componentMasks; + private uint _fragmentOutputMap; + private uint _componentMasks; + private uint _currentComponentMasks; private uint _scissorEnables; @@ -73,12 +75,8 @@ namespace Ryujinx.Graphics.OpenGL _clipOrigin = ClipOrigin.LowerLeft; _clipDepthMode = ClipDepthMode.NegativeOneToOne; - _componentMasks = new uint[Constants.MaxRenderTargets]; - - for (int index = 0; index < Constants.MaxRenderTargets; index++) - { - _componentMasks[index] = 0xf; - } + _fragmentOutputMap = uint.MaxValue; + _componentMasks = uint.MaxValue; var defaultScale = new Vector4 { X = 1f, Y = 0f, Z = 0f, W = 0f }; new Span>(_renderScale).Fill(defaultScale); @@ -1001,18 +999,30 @@ namespace Ryujinx.Graphics.OpenGL public void SetProgram(IProgram program) { - _program = (Program)program; + Program prg = (Program)program; if (_tfEnabled) { GL.EndTransformFeedback(); - _program.Bind(); + prg.Bind(); GL.BeginTransformFeedback(_tfTopology); } else { - _program.Bind(); + prg.Bind(); } + + if (prg.HasFragmentShader && _fragmentOutputMap != (uint)prg.FragmentOutputMap) + { + _fragmentOutputMap = (uint)prg.FragmentOutputMap; + + for (int index = 0; index < Constants.MaxRenderTargets; index++) + { + RestoreComponentMask(index, force: false); + } + } + + _program = prg; } public void SetRasterizerDiscard(bool discard) @@ -1037,11 +1047,13 @@ namespace Ryujinx.Graphics.OpenGL public void SetRenderTargetColorMasks(ReadOnlySpan componentMasks) { + _componentMasks = 0; + for (int index = 0; index < componentMasks.Length; index++) { - _componentMasks[index] = componentMasks[index]; + _componentMasks |= componentMasks[index] << (index * 4); - RestoreComponentMask(index); + RestoreComponentMask(index, force: false); } } @@ -1436,18 +1448,34 @@ namespace Ryujinx.Graphics.OpenGL } } - public void RestoreComponentMask(int index) + public void RestoreComponentMask(int index, bool force = true) { // If the bound render target is bgra, swap the red and blue masks. uint redMask = _fpIsBgra[index].X == 0 ? 1u : 4u; uint blueMask = _fpIsBgra[index].X == 0 ? 4u : 1u; + int shift = index * 4; + uint componentMask = _componentMasks & _fragmentOutputMap; + uint checkMask = 0xfu << shift; + uint componentMaskAtIndex = componentMask & checkMask; + + if (!force && componentMaskAtIndex == (_currentComponentMasks & checkMask)) + { + return; + } + + componentMask >>= shift; + componentMask &= 0xfu; + GL.ColorMask( index, - (_componentMasks[index] & redMask) != 0, - (_componentMasks[index] & 2u) != 0, - (_componentMasks[index] & blueMask) != 0, - (_componentMasks[index] & 8u) != 0); + (componentMask & redMask) != 0, + (componentMask & 2u) != 0, + (componentMask & blueMask) != 0, + (componentMask & 8u) != 0); + + _currentComponentMasks &= ~checkMask; + _currentComponentMasks |= componentMaskAtIndex; } public void RestoreScissor0Enable() diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs index 959029903..838162ccd 100644 --- a/Ryujinx.Graphics.OpenGL/Program.cs +++ b/Ryujinx.Graphics.OpenGL/Program.cs @@ -1,11 +1,8 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader.CodeGen.Glsl; using System; using System.Buffers.Binary; -using System.Collections.Generic; -using System.Linq; namespace Ryujinx.Graphics.OpenGL { @@ -29,7 +26,10 @@ namespace Ryujinx.Graphics.OpenGL private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete; private IShader[] _shaders; - public Program(IShader[] shaders) + public bool HasFragmentShader; + public int FragmentOutputMap { get; } + + public Program(IShader[] shaders, int fragmentOutputMap) { Handle = GL.CreateProgram(); @@ -37,17 +37,23 @@ namespace Ryujinx.Graphics.OpenGL for (int index = 0; index < shaders.Length; index++) { - int shaderHandle = ((Shader)shaders[index]).Handle; + Shader shader = (Shader)shaders[index]; - GL.AttachShader(Handle, shaderHandle); + if (shader.IsFragment) + { + HasFragmentShader = true; + } + + GL.AttachShader(Handle, shader.Handle); } GL.LinkProgram(Handle); _shaders = shaders; + FragmentOutputMap = fragmentOutputMap; } - public Program(ReadOnlySpan code) + public Program(ReadOnlySpan code, bool hasFragmentShader, int fragmentOutputMap) { BinaryFormat binaryFormat = (BinaryFormat)BinaryPrimitives.ReadInt32LittleEndian(code.Slice(code.Length - 4, 4)); @@ -60,6 +66,9 @@ namespace Ryujinx.Graphics.OpenGL GL.ProgramBinary(Handle, binaryFormat, (IntPtr)ptr, code.Length - 4); } } + + HasFragmentShader = hasFragmentShader; + FragmentOutputMap = fragmentOutputMap; } public void Bind() diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index 8d44f2e44..a99ecfcc2 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -66,9 +66,9 @@ namespace Ryujinx.Graphics.OpenGL return Buffer.Create(size); } - public IProgram CreateProgram(IShader[] shaders) + public IProgram CreateProgram(IShader[] shaders, ShaderInfo info) { - return new Program(shaders); + return new Program(shaders, info.FragmentOutputMap); } public ISampler CreateSampler(SamplerCreateInfo info) @@ -202,9 +202,9 @@ namespace Ryujinx.Graphics.OpenGL _sync.Dispose(); } - public IProgram LoadProgramBinary(byte[] programBinary) + public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) { - return new Program(programBinary); + return new Program(programBinary, hasFragmentShader, info.FragmentOutputMap); } public void CreateSync(ulong id) diff --git a/Ryujinx.Graphics.OpenGL/Shader.cs b/Ryujinx.Graphics.OpenGL/Shader.cs index 1df07ee42..8374fa626 100644 --- a/Ryujinx.Graphics.OpenGL/Shader.cs +++ b/Ryujinx.Graphics.OpenGL/Shader.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.OpenGL class Shader : IShader { public int Handle { get; private set; } + public bool IsFragment { get; } public Shader(ShaderStage stage, string code) { @@ -22,6 +23,7 @@ namespace Ryujinx.Graphics.OpenGL }; Handle = GL.CreateShader(type); + IsFragment = stage == ShaderStage.Fragment; GL.ShaderSource(Handle, code); GL.CompileShader(Handle); diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs index a9ce486b6..d1c1b9457 100644 --- a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -5,32 +5,35 @@ namespace Ryujinx.Graphics.Shader { public class ShaderProgramInfo { - public ReadOnlyCollection CBuffers { get; } - public ReadOnlyCollection SBuffers { get; } + public ReadOnlyCollection CBuffers { get; } + public ReadOnlyCollection SBuffers { get; } public ReadOnlyCollection Textures { get; } - public ReadOnlyCollection Images { get; } + public ReadOnlyCollection Images { get; } public bool UsesInstanceId { get; } public bool UsesRtLayer { get; } public byte ClipDistancesWritten { get; } + public int FragmentOutputMap { get; } public ShaderProgramInfo( - BufferDescriptor[] cBuffers, - BufferDescriptor[] sBuffers, + BufferDescriptor[] cBuffers, + BufferDescriptor[] sBuffers, TextureDescriptor[] textures, TextureDescriptor[] images, - bool usesInstanceId, - bool usesRtLayer, - byte clipDistancesWritten) + bool usesInstanceId, + bool usesRtLayer, + byte clipDistancesWritten, + int fragmentOutputMap) { CBuffers = Array.AsReadOnly(cBuffers); SBuffers = Array.AsReadOnly(sBuffers); Textures = Array.AsReadOnly(textures); - Images = Array.AsReadOnly(images); + Images = Array.AsReadOnly(images); UsesInstanceId = usesInstanceId; UsesRtLayer = usesRtLayer; ClipDistancesWritten = clipDistancesWritten; + FragmentOutputMap = fragmentOutputMap; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 3dcb04ade..775f12179 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -172,11 +172,10 @@ namespace Ryujinx.Graphics.Shader.Translation for (int rtIndex = 0; rtIndex < 8; rtIndex++) { - OmapTarget target = Config.OmapTargets[rtIndex]; - for (int component = 0; component < 4; component++) { - if (!target.ComponentEnabled(component)) + bool componentEnabled = (Config.OmapTargets & (1 << (rtIndex * 4 + component))) != 0; + if (!componentEnabled) { continue; } @@ -210,7 +209,8 @@ namespace Ryujinx.Graphics.Shader.Translation } } - if (target.Enabled) + bool targetEnabled = (Config.OmapTargets & (0xf << (rtIndex * 4))) != 0; + if (targetEnabled) { Config.SetOutputUserAttribute(rtIndex, perPatch: false); regIndexBase += 4; diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 21f170414..996c2814b 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -25,9 +25,9 @@ namespace Ryujinx.Graphics.Shader.Translation public ImapPixelType[] ImapTypes { get; } - public OmapTarget[] OmapTargets { get; } - public bool OmapSampleMask { get; } - public bool OmapDepth { get; } + public int OmapTargets { get; } + public bool OmapSampleMask { get; } + public bool OmapDepth { get; } public IGpuAccessor GpuAccessor { get; } @@ -135,21 +135,8 @@ namespace Ryujinx.Graphics.Shader.Translation public int GetDepthRegister() { - int count = 0; - - for (int index = 0; index < OmapTargets.Length; index++) - { - for (int component = 0; component < 4; component++) - { - if (OmapTargets[index].ComponentEnabled(component)) - { - count++; - } - } - } - // The depth register is always two registers after the last color output. - return count + 1; + return BitOperations.PopCount((uint)OmapTargets) + 1; } public TextureFormat GetTextureFormat(int handle, int cbufSlot = -1) diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs b/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs index 0ad172da8..e53c77af0 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs @@ -36,37 +36,6 @@ namespace Ryujinx.Graphics.Shader.Translation } } - struct OmapTarget - { - public bool Red { get; } - public bool Green { get; } - public bool Blue { get; } - public bool Alpha { get; } - - public bool Enabled => Red || Green || Blue || Alpha; - - public OmapTarget(bool red, bool green, bool blue, bool alpha) - { - Red = red; - Green = green; - Blue = blue; - Alpha = alpha; - } - - public bool ComponentEnabled(int component) - { - switch (component) - { - case 0: return Red; - case 1: return Green; - case 2: return Blue; - case 3: return Alpha; - } - - throw new ArgumentOutOfRangeException(nameof(component)); - } - } - class ShaderHeader { public int SphType { get; } @@ -85,7 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation public bool GpPassthrough { get; } public bool DoesLoadOrStore { get; } - public bool DoesFp64 { get; } + public bool DoesFp64 { get; } public int StreamOutMask { get; } @@ -104,13 +73,13 @@ namespace Ryujinx.Graphics.Shader.Translation public int MaxOutputVertexCount { get; } public int StoreReqStart { get; } - public int StoreReqEnd { get; } + public int StoreReqEnd { get; } public ImapPixelType[] ImapTypes { get; } - public OmapTarget[] OmapTargets { get; } - public bool OmapSampleMask { get; } - public bool OmapDepth { get; } + public int OmapTargets { get; } + public bool OmapSampleMask { get; } + public bool OmapDepth { get; } public ShaderHeader(IGpuAccessor gpuAccessor, ulong address) { @@ -144,7 +113,7 @@ namespace Ryujinx.Graphics.Shader.Translation GpPassthrough = commonWord0.Extract(24); DoesLoadOrStore = commonWord0.Extract(26); - DoesFp64 = commonWord0.Extract(27); + DoesFp64 = commonWord0.Extract(27); StreamOutMask = commonWord0.Extract(28, 4); @@ -163,7 +132,7 @@ namespace Ryujinx.Graphics.Shader.Translation MaxOutputVertexCount = commonWord4.Extract(0, 12); StoreReqStart = commonWord4.Extract(12, 8); - StoreReqEnd = commonWord4.Extract(24, 8); + StoreReqEnd = commonWord4.Extract(24, 8); ImapTypes = new ImapPixelType[32]; @@ -179,21 +148,11 @@ namespace Ryujinx.Graphics.Shader.Translation } int type2OmapTarget = header[18]; - int type2Omap = header[19]; - - OmapTargets = new OmapTarget[8]; - - for (int offset = 0; offset < OmapTargets.Length * 4; offset += 4) - { - OmapTargets[offset >> 2] = new OmapTarget( - type2OmapTarget.Extract(offset + 0), - type2OmapTarget.Extract(offset + 1), - type2OmapTarget.Extract(offset + 2), - type2OmapTarget.Extract(offset + 3)); - } + int type2Omap = header[19]; + OmapTargets = type2OmapTarget; OmapSampleMask = type2Omap.Extract(0); - OmapDepth = type2Omap.Extract(1); + OmapDepth = type2Omap.Extract(1); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 709b16db4..603b20d64 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -105,7 +105,8 @@ namespace Ryujinx.Graphics.Shader.Translation config.GetImageDescriptors(), config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), config.UsedFeatures.HasFlag(FeatureFlags.RtLayer), - config.ClipDistancesWritten); + config.ClipDistancesWritten, + config.OmapTargets); return program; }