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:
parent
ab5d77c0c4
commit
3bd357045f
17 changed files with 176 additions and 131 deletions
|
@ -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<byte> data);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<CreateProgramCommand>().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<CreateProgramCommand>().Set(Ref((IProgramRequest)request));
|
||||
|
|
12
Ryujinx.Graphics.GAL/ShaderInfo.cs
Normal file
12
Ryujinx.Graphics.GAL/ShaderInfo.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public struct ShaderInfo
|
||||
{
|
||||
public int FragmentOutputMap { get; }
|
||||
|
||||
public ShaderInfo(int fragmentOutputMap)
|
||||
{
|
||||
FragmentOutputMap = fragmentOutputMap;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
|
|||
/// <summary>
|
||||
/// Host shader entry header used for binding information.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x14)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
|
||||
struct HostShaderCacheEntryHeader
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -70,6 +70,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
|
|||
/// </summary>
|
||||
public byte Reserved;
|
||||
|
||||
/// <summary>
|
||||
/// Mask of components written by the fragment shader stage.
|
||||
/// </summary>
|
||||
public int FragmentOutputMap;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new host shader cache entry header.
|
||||
/// </summary>
|
||||
|
@ -78,6 +83,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
|
|||
/// <param name="texturesCount">Count of texture 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="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(
|
||||
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;
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <summary>
|
||||
/// Version of the codegen (to be changed when codegen or guest format change).
|
||||
/// </summary>
|
||||
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<byte> 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<byte> 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);
|
||||
|
||||
|
|
|
@ -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<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
|
||||
new Span<Vector4<float>>(_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<uint> 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()
|
||||
|
|
|
@ -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<byte> code)
|
||||
public Program(ReadOnlySpan<byte> 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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
public bool UsesInstanceId { get; }
|
||||
public bool UsesRtLayer { get; }
|
||||
public byte ClipDistancesWritten { get; }
|
||||
public int FragmentOutputMap { get; }
|
||||
|
||||
public ShaderProgramInfo(
|
||||
BufferDescriptor[] cBuffers,
|
||||
|
@ -21,7 +22,8 @@ namespace Ryujinx.Graphics.Shader
|
|||
TextureDescriptor[] images,
|
||||
bool usesInstanceId,
|
||||
bool usesRtLayer,
|
||||
byte clipDistancesWritten)
|
||||
byte clipDistancesWritten,
|
||||
int fragmentOutputMap)
|
||||
{
|
||||
CBuffers = Array.AsReadOnly(cBuffers);
|
||||
SBuffers = Array.AsReadOnly(sBuffers);
|
||||
|
@ -31,6 +33,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
UsesInstanceId = usesInstanceId;
|
||||
UsesRtLayer = usesRtLayer;
|
||||
ClipDistancesWritten = clipDistancesWritten;
|
||||
FragmentOutputMap = fragmentOutputMap;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public ImapPixelType[] ImapTypes { get; }
|
||||
|
||||
public OmapTarget[] OmapTargets { get; }
|
||||
public int OmapTargets { get; }
|
||||
public bool OmapSampleMask { get; }
|
||||
public bool OmapDepth { 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)
|
||||
|
|
|
@ -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; }
|
||||
|
@ -108,7 +77,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public ImapPixelType[] ImapTypes { get; }
|
||||
|
||||
public OmapTarget[] OmapTargets { get; }
|
||||
public int OmapTargets { get; }
|
||||
public bool OmapSampleMask { get; }
|
||||
public bool OmapDepth { get; }
|
||||
|
||||
|
@ -181,17 +150,7 @@ 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));
|
||||
}
|
||||
|
||||
OmapTargets = type2OmapTarget;
|
||||
OmapSampleMask = type2Omap.Extract(0);
|
||||
OmapDepth = type2Omap.Extract(1);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Reference in a new issue