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
This commit is contained in:
gdkchan 2022-02-16 19:15:39 -03:00 committed by GitHub
parent ab5d77c0c4
commit 3bd357045f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 176 additions and 131 deletions

View file

@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.GAL
BufferHandle CreateBuffer(int size); BufferHandle CreateBuffer(int size);
IProgram CreateProgram(IShader[] shaders); IProgram CreateProgram(IShader[] shaders, ShaderInfo info);
ISampler CreateSampler(SamplerCreateInfo info); ISampler CreateSampler(SamplerCreateInfo info);
ITexture CreateTexture(TextureCreateInfo info, float scale); ITexture CreateTexture(TextureCreateInfo info, float scale);
@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.GAL
Capabilities GetCapabilities(); Capabilities GetCapabilities();
IProgram LoadProgramBinary(byte[] programBinary); IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info);
void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data); void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data);

View file

@ -5,17 +5,21 @@
public ThreadedProgram Threaded { get; set; } public ThreadedProgram Threaded { get; set; }
private byte[] _data; 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; Threaded = program;
_data = data; _data = data;
_hasFragmentShader = hasFragmentShader;
_info = info;
} }
public IProgram Create(IRenderer renderer) public IProgram Create(IRenderer renderer)
{ {
return renderer.LoadProgramBinary(_data); return renderer.LoadProgramBinary(_data, _hasFragmentShader, _info);
} }
} }
} }

View file

@ -7,12 +7,14 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs
public ThreadedProgram Threaded { get; set; } public ThreadedProgram Threaded { get; set; }
private IShader[] _shaders; private IShader[] _shaders;
private ShaderInfo _info;
public SourceProgramRequest(ThreadedProgram program, IShader[] shaders) public SourceProgramRequest(ThreadedProgram program, IShader[] shaders, ShaderInfo info)
{ {
Threaded = program; Threaded = program;
_shaders = shaders; _shaders = shaders;
_info = info;
} }
public IProgram Create(IRenderer renderer) public IProgram Create(IRenderer renderer)
@ -24,7 +26,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs
return threaded?.Base; return threaded?.Base;
}).ToArray(); }).ToArray();
return renderer.CreateProgram(shaders); return renderer.CreateProgram(shaders, _info);
} }
} }
} }

View file

@ -268,10 +268,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
return handle; return handle;
} }
public IProgram CreateProgram(IShader[] shaders) public IProgram CreateProgram(IShader[] shaders, ShaderInfo info)
{ {
var program = new ThreadedProgram(this); var program = new ThreadedProgram(this);
SourceProgramRequest request = new SourceProgramRequest(program, shaders); SourceProgramRequest request = new SourceProgramRequest(program, shaders, info);
Programs.Add(request); Programs.Add(request);
New<CreateProgramCommand>().Set(Ref((IProgramRequest)request)); New<CreateProgramCommand>().Set(Ref((IProgramRequest)request));
@ -355,11 +355,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_baseRenderer.Initialize(logLevel); _baseRenderer.Initialize(logLevel);
} }
public IProgram LoadProgramBinary(byte[] programBinary) public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info)
{ {
var program = new ThreadedProgram(this); var program = new ThreadedProgram(this);
BinaryProgramRequest request = new BinaryProgramRequest(program, programBinary); BinaryProgramRequest request = new BinaryProgramRequest(program, programBinary, hasFragmentShader, info);
Programs.Add(request); Programs.Add(request);
New<CreateProgramCommand>().Set(Ref((IProgramRequest)request)); New<CreateProgramCommand>().Set(Ref((IProgramRequest)request));

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.GAL
{
public struct ShaderInfo
{
public int FragmentOutputMap { get; }
public ShaderInfo(int fragmentOutputMap)
{
FragmentOutputMap = fragmentOutputMap;
}
}
}

View file

@ -77,7 +77,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
programInfo.Images.Count, programInfo.Images.Count,
programInfo.UsesInstanceId, programInfo.UsesInstanceId,
programInfo.UsesRtLayer, programInfo.UsesRtLayer,
programInfo.ClipDistancesWritten); programInfo.ClipDistancesWritten,
programInfo.FragmentOutputMap);
CBuffers = programInfo.CBuffers.ToArray(); CBuffers = programInfo.CBuffers.ToArray();
SBuffers = programInfo.SBuffers.ToArray(); SBuffers = programInfo.SBuffers.ToArray();
Textures = programInfo.Textures.ToArray(); Textures = programInfo.Textures.ToArray();
@ -97,7 +98,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
Images, Images,
Header.UseFlags.HasFlag(UseFlags.InstanceId), Header.UseFlags.HasFlag(UseFlags.InstanceId),
Header.UseFlags.HasFlag(UseFlags.RtLayer), Header.UseFlags.HasFlag(UseFlags.RtLayer),
Header.ClipDistancesWritten); Header.ClipDistancesWritten,
Header.FragmentOutputMap);
} }
/// <summary> /// <summary>

View file

@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
/// <summary> /// <summary>
/// Host shader entry header used for binding information. /// Host shader entry header used for binding information.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x14)] [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
struct HostShaderCacheEntryHeader struct HostShaderCacheEntryHeader
{ {
/// <summary> /// <summary>
@ -70,6 +70,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
/// </summary> /// </summary>
public byte Reserved; public byte Reserved;
/// <summary>
/// Mask of components written by the fragment shader stage.
/// </summary>
public int FragmentOutputMap;
/// <summary> /// <summary>
/// Create a new host shader cache entry header. /// Create a new host shader cache entry header.
/// </summary> /// </summary>
@ -78,6 +83,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
/// <param name="texturesCount">Count of texture descriptors</param> /// <param name="texturesCount">Count of texture descriptors</param>
/// <param name="imagesCount">Count of image descriptors</param> /// <param name="imagesCount">Count of image descriptors</param>
/// <param name="usesInstanceId">Set to true if the shader uses instance id</param> /// <param name="usesInstanceId">Set to true if the shader uses instance id</param>
/// <param name="clipDistancesWritten">Mask of clip distances that are written to on the shader</param>
/// <param name="fragmentOutputMap">Mask of components written by the fragment shader stage</param>
public HostShaderCacheEntryHeader( public HostShaderCacheEntryHeader(
int cBuffersCount, int cBuffersCount,
int sBuffersCount, int sBuffersCount,
@ -85,13 +92,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
int imagesCount, int imagesCount,
bool usesInstanceId, bool usesInstanceId,
bool usesRtLayer, bool usesRtLayer,
byte clipDistancesWritten) : this() byte clipDistancesWritten,
int fragmentOutputMap) : this()
{ {
CBuffersCount = cBuffersCount; CBuffersCount = cBuffersCount;
SBuffersCount = sBuffersCount; SBuffersCount = sBuffersCount;
TexturesCount = texturesCount; TexturesCount = texturesCount;
ImagesCount = imagesCount; ImagesCount = imagesCount;
ClipDistancesWritten = clipDistancesWritten; ClipDistancesWritten = clipDistancesWritten;
FragmentOutputMap = fragmentOutputMap;
InUse = true; InUse = true;
UseFlags = usesInstanceId ? UseFlags.InstanceId : UseFlags.None; UseFlags = usesInstanceId ? UseFlags.InstanceId : UseFlags.None;

View file

@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Version of the codegen (to be changed when codegen or guest format change). /// Version of the codegen (to be changed when codegen or guest format change).
/// </summary> /// </summary>
private const ulong ShaderCodeGenVersion = 3106; private const ulong ShaderCodeGenVersion = 3063;
// Progress reporting helpers // Progress reporting helpers
private volatile int _shaderCount; private volatile int _shaderCount;
@ -188,7 +188,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan<byte> hostProgramBinarySpan); hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan<byte> hostProgramBinarySpan);
hostProgramBinary = hostProgramBinarySpan.ToArray(); hostProgramBinary = hostProgramBinarySpan.ToArray();
hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary, false, new ShaderInfo(-1));
} }
ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); 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. // Compile shader and create program as the shader program binary got invalidated.
shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, program.Code); 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) => task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) =>
{ {
@ -303,7 +303,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan<byte> hostProgramBinarySpan); hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan<byte> hostProgramBinarySpan);
hostProgramBinary = hostProgramBinarySpan.ToArray(); 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); ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent);
@ -426,7 +437,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
hostShaders.Add(hostShader); 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) => 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); 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); cpShader = new ShaderBundle(hostProgram, shader);
@ -755,7 +774,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
hostShaders.Add(hostShader); 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); gpShaders = new ShaderBundle(hostProgram, shaders);

View file

@ -53,7 +53,9 @@ namespace Ryujinx.Graphics.OpenGL
private ClipOrigin _clipOrigin; private ClipOrigin _clipOrigin;
private ClipDepthMode _clipDepthMode; private ClipDepthMode _clipDepthMode;
private readonly uint[] _componentMasks; private uint _fragmentOutputMap;
private uint _componentMasks;
private uint _currentComponentMasks;
private uint _scissorEnables; private uint _scissorEnables;
@ -73,12 +75,8 @@ namespace Ryujinx.Graphics.OpenGL
_clipOrigin = ClipOrigin.LowerLeft; _clipOrigin = ClipOrigin.LowerLeft;
_clipDepthMode = ClipDepthMode.NegativeOneToOne; _clipDepthMode = ClipDepthMode.NegativeOneToOne;
_componentMasks = new uint[Constants.MaxRenderTargets]; _fragmentOutputMap = uint.MaxValue;
_componentMasks = uint.MaxValue;
for (int index = 0; index < Constants.MaxRenderTargets; index++)
{
_componentMasks[index] = 0xf;
}
var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f }; var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
new Span<Vector4<float>>(_renderScale).Fill(defaultScale); new Span<Vector4<float>>(_renderScale).Fill(defaultScale);
@ -1001,18 +999,30 @@ namespace Ryujinx.Graphics.OpenGL
public void SetProgram(IProgram program) public void SetProgram(IProgram program)
{ {
_program = (Program)program; Program prg = (Program)program;
if (_tfEnabled) if (_tfEnabled)
{ {
GL.EndTransformFeedback(); GL.EndTransformFeedback();
_program.Bind(); prg.Bind();
GL.BeginTransformFeedback(_tfTopology); GL.BeginTransformFeedback(_tfTopology);
} }
else 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) public void SetRasterizerDiscard(bool discard)
@ -1037,11 +1047,13 @@ namespace Ryujinx.Graphics.OpenGL
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
{ {
_componentMasks = 0;
for (int index = 0; index < componentMasks.Length; index++) 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. // If the bound render target is bgra, swap the red and blue masks.
uint redMask = _fpIsBgra[index].X == 0 ? 1u : 4u; uint redMask = _fpIsBgra[index].X == 0 ? 1u : 4u;
uint blueMask = _fpIsBgra[index].X == 0 ? 4u : 1u; 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( GL.ColorMask(
index, index,
(_componentMasks[index] & redMask) != 0, (componentMask & redMask) != 0,
(_componentMasks[index] & 2u) != 0, (componentMask & 2u) != 0,
(_componentMasks[index] & blueMask) != 0, (componentMask & blueMask) != 0,
(_componentMasks[index] & 8u) != 0); (componentMask & 8u) != 0);
_currentComponentMasks &= ~checkMask;
_currentComponentMasks |= componentMaskAtIndex;
} }
public void RestoreScissor0Enable() public void RestoreScissor0Enable()

View file

@ -1,11 +1,8 @@
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.OpenGL namespace Ryujinx.Graphics.OpenGL
{ {
@ -29,7 +26,10 @@ namespace Ryujinx.Graphics.OpenGL
private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete; private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete;
private IShader[] _shaders; private IShader[] _shaders;
public Program(IShader[] shaders) public bool HasFragmentShader;
public int FragmentOutputMap { get; }
public Program(IShader[] shaders, int fragmentOutputMap)
{ {
Handle = GL.CreateProgram(); Handle = GL.CreateProgram();
@ -37,17 +37,23 @@ namespace Ryujinx.Graphics.OpenGL
for (int index = 0; index < shaders.Length; index++) 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); GL.LinkProgram(Handle);
_shaders = shaders; _shaders = shaders;
FragmentOutputMap = fragmentOutputMap;
} }
public Program(ReadOnlySpan<byte> code) public Program(ReadOnlySpan<byte> code, bool hasFragmentShader, int fragmentOutputMap)
{ {
BinaryFormat binaryFormat = (BinaryFormat)BinaryPrimitives.ReadInt32LittleEndian(code.Slice(code.Length - 4, 4)); 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); GL.ProgramBinary(Handle, binaryFormat, (IntPtr)ptr, code.Length - 4);
} }
} }
HasFragmentShader = hasFragmentShader;
FragmentOutputMap = fragmentOutputMap;
} }
public void Bind() public void Bind()

View file

@ -66,9 +66,9 @@ namespace Ryujinx.Graphics.OpenGL
return Buffer.Create(size); 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) public ISampler CreateSampler(SamplerCreateInfo info)
@ -202,9 +202,9 @@ namespace Ryujinx.Graphics.OpenGL
_sync.Dispose(); _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) public void CreateSync(ulong id)

View file

@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.OpenGL
class Shader : IShader class Shader : IShader
{ {
public int Handle { get; private set; } public int Handle { get; private set; }
public bool IsFragment { get; }
public Shader(ShaderStage stage, string code) public Shader(ShaderStage stage, string code)
{ {
@ -22,6 +23,7 @@ namespace Ryujinx.Graphics.OpenGL
}; };
Handle = GL.CreateShader(type); Handle = GL.CreateShader(type);
IsFragment = stage == ShaderStage.Fragment;
GL.ShaderSource(Handle, code); GL.ShaderSource(Handle, code);
GL.CompileShader(Handle); GL.CompileShader(Handle);

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Shader
public bool UsesInstanceId { get; } public bool UsesInstanceId { get; }
public bool UsesRtLayer { get; } public bool UsesRtLayer { get; }
public byte ClipDistancesWritten { get; } public byte ClipDistancesWritten { get; }
public int FragmentOutputMap { get; }
public ShaderProgramInfo( public ShaderProgramInfo(
BufferDescriptor[] cBuffers, BufferDescriptor[] cBuffers,
@ -21,7 +22,8 @@ namespace Ryujinx.Graphics.Shader
TextureDescriptor[] images, TextureDescriptor[] images,
bool usesInstanceId, bool usesInstanceId,
bool usesRtLayer, bool usesRtLayer,
byte clipDistancesWritten) byte clipDistancesWritten,
int fragmentOutputMap)
{ {
CBuffers = Array.AsReadOnly(cBuffers); CBuffers = Array.AsReadOnly(cBuffers);
SBuffers = Array.AsReadOnly(sBuffers); SBuffers = Array.AsReadOnly(sBuffers);
@ -31,6 +33,7 @@ namespace Ryujinx.Graphics.Shader
UsesInstanceId = usesInstanceId; UsesInstanceId = usesInstanceId;
UsesRtLayer = usesRtLayer; UsesRtLayer = usesRtLayer;
ClipDistancesWritten = clipDistancesWritten; ClipDistancesWritten = clipDistancesWritten;
FragmentOutputMap = fragmentOutputMap;
} }
} }
} }

View file

@ -172,11 +172,10 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int rtIndex = 0; rtIndex < 8; rtIndex++) for (int rtIndex = 0; rtIndex < 8; rtIndex++)
{ {
OmapTarget target = Config.OmapTargets[rtIndex];
for (int component = 0; component < 4; component++) for (int component = 0; component < 4; component++)
{ {
if (!target.ComponentEnabled(component)) bool componentEnabled = (Config.OmapTargets & (1 << (rtIndex * 4 + component))) != 0;
if (!componentEnabled)
{ {
continue; 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); Config.SetOutputUserAttribute(rtIndex, perPatch: false);
regIndexBase += 4; regIndexBase += 4;

View file

@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ImapPixelType[] ImapTypes { get; } public ImapPixelType[] ImapTypes { get; }
public OmapTarget[] OmapTargets { get; } public int OmapTargets { get; }
public bool OmapSampleMask { get; } public bool OmapSampleMask { get; }
public bool OmapDepth { get; } public bool OmapDepth { get; }
@ -135,21 +135,8 @@ namespace Ryujinx.Graphics.Shader.Translation
public int GetDepthRegister() 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. // 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) public TextureFormat GetTextureFormat(int handle, int cbufSlot = -1)

View file

@ -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 class ShaderHeader
{ {
public int SphType { get; } public int SphType { get; }
@ -108,7 +77,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ImapPixelType[] ImapTypes { get; } public ImapPixelType[] ImapTypes { get; }
public OmapTarget[] OmapTargets { get; } public int OmapTargets { get; }
public bool OmapSampleMask { get; } public bool OmapSampleMask { get; }
public bool OmapDepth { get; } public bool OmapDepth { get; }
@ -181,17 +150,7 @@ namespace Ryujinx.Graphics.Shader.Translation
int type2OmapTarget = header[18]; int type2OmapTarget = header[18];
int type2Omap = header[19]; int type2Omap = header[19];
OmapTargets = new OmapTarget[8]; OmapTargets = type2OmapTarget;
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));
}
OmapSampleMask = type2Omap.Extract(0); OmapSampleMask = type2Omap.Extract(0);
OmapDepth = type2Omap.Extract(1); OmapDepth = type2Omap.Extract(1);
} }

View file

@ -105,7 +105,8 @@ namespace Ryujinx.Graphics.Shader.Translation
config.GetImageDescriptors(), config.GetImageDescriptors(),
config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), config.UsedFeatures.HasFlag(FeatureFlags.InstanceId),
config.UsedFeatures.HasFlag(FeatureFlags.RtLayer), config.UsedFeatures.HasFlag(FeatureFlags.RtLayer),
config.ClipDistancesWritten); config.ClipDistancesWritten,
config.OmapTargets);
return program; return program;
} }