Compare commits

..

4 Commits

Author SHA1 Message Date
Emmanuel Hansen
bc5bb4459e Fix deadlock in mouse input on Avalonia (#3444)
* fix deadlock in mouse input

* apply @AcK77 changes
2022-07-08 09:53:48 -03:00
gdkchan
55e97959b9 Fix Vi managed and stray layers open/close/destroy (#3438)
* Fix Vi managed and stray layers open/close/destroy

* OpenLayer should set the state to ManagedOpened
2022-07-06 13:37:36 -03:00
gdkchan
f7ef6364b7 Implement CPU FCVT Half <-> Double conversion variants (#3439)
* Half <-> Double conversion support

* Add tests, fast path and deduplicate SoftFloat code

* PPTC version
2022-07-06 13:40:31 +02:00
gdkchan
b46b63e06a Add support for alpha to coverage dithering (#3069)
* Add support for alpha to coverage dithering

* Shader cache version bump

* Fix wrong alpha register

* Ensure support buffer is cleared

* New shader specialization based approach
2022-07-05 19:58:36 -03:00
31 changed files with 1094 additions and 500 deletions

View File

@@ -105,11 +105,48 @@ namespace ARMeilleure.Instructions
}
else if (op.Size == 1 && op.Opc == 3) // Double -> Half.
{
throw new NotImplementedException("Double-precision to half-precision.");
if (Optimizations.UseF16c)
{
Debug.Assert(!Optimizations.ForceLegacySse);
Operand n = GetVec(op.Rn);
Operand res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), n);
res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
context.Copy(GetVec(op.Rd), res);
}
else
{
Operand ne = context.VectorExtract(OperandType.FP64, GetVec(op.Rn), 0);
Operand res = context.Call(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)), ne);
res = context.ZeroExtend16(OperandType.I64, res);
context.Copy(GetVec(op.Rd), EmitVectorInsert(context, context.VectorZero(), res, 0, 1));
}
}
else if (op.Size == 3 && op.Opc == 1) // Double -> Half.
else if (op.Size == 3 && op.Opc == 1) // Half -> Double.
{
throw new NotImplementedException("Half-precision to double-precision.");
if (Optimizations.UseF16c)
{
Operand n = GetVec(op.Rn);
Operand res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, GetVec(op.Rn));
res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res);
res = context.VectorZeroUpper64(res);
context.Copy(GetVec(op.Rd), res);
}
else
{
Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1);
Operand res = context.Call(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)), ne);
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
}
}
else // Invalid encoding.
{

File diff suppressed because it is too large Load Diff

View File

@@ -206,6 +206,7 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedSrcUnsignedDstSatQ)));
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only.
@@ -294,6 +295,8 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)));
}
}
}

View File

@@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 3362; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 3439; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";

View File

@@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
@@ -13,6 +14,7 @@ namespace Ryujinx.Ava.Input
{
private Control _widget;
private bool _isDisposed;
private Size _size;
public bool[] PressedButtons { get; }
@@ -29,6 +31,14 @@ namespace Ryujinx.Ava.Input
_widget.PointerWheelChanged += Parent_ScrollEvent;
PressedButtons = new bool[(int)MouseButton.Count];
_size = new Size((int)parent.Bounds.Width, (int)parent.Bounds.Height);
parent.GetObservable(Control.BoundsProperty).Subscribe(Resized);
}
private void Resized(Rect rect)
{
_size = new Size((int)rect.Width, (int)rect.Height);
}
private void Parent_ScrollEvent(object o, PointerWheelEventArgs args)
@@ -78,14 +88,7 @@ namespace Ryujinx.Ava.Input
public Size GetClientSize()
{
Size size = new();
Dispatcher.UIThread.InvokeAsync(() =>
{
size = new Size((int)_widget.Bounds.Width, (int)_widget.Bounds.Height);
}).Wait();
return size;
return _size;
}
public string DriverName => "Avalonia";

View File

@@ -60,6 +60,8 @@ namespace Ryujinx.Graphics.GAL
void SetLogicOpState(bool enable, LogicalOp op);
void SetMultisampleState(MultisampleDescriptor multisample);
void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel);
void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin);

View File

@@ -0,0 +1,19 @@
namespace Ryujinx.Graphics.GAL
{
public struct MultisampleDescriptor
{
public bool AlphaToCoverageEnable { get; }
public bool AlphaToCoverageDitherEnable { get; }
public bool AlphaToOneEnable { get; }
public MultisampleDescriptor(
bool alphaToCoverageEnable,
bool alphaToCoverageDitherEnable,
bool alphaToOneEnable)
{
AlphaToCoverageEnable = alphaToCoverageEnable;
AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable;
AlphaToOneEnable = alphaToOneEnable;
}
}
}

View File

@@ -179,6 +179,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
SetLineParametersCommand.Run(ref GetCommand<SetLineParametersCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.SetLogicOpState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
SetLogicOpStateCommand.Run(ref GetCommand<SetLogicOpStateCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.SetMultisampleState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
SetMultisampleStateCommand.Run(ref GetCommand<SetMultisampleStateCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.SetPatchParameters] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
SetPatchParametersCommand.Run(ref GetCommand<SetPatchParametersCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.SetPointParameters] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>

View File

@@ -71,6 +71,7 @@
SetIndexBuffer,
SetLineParameters,
SetLogicOpState,
SetMultisampleState,
SetPatchParameters,
SetPointParameters,
SetPolygonMode,

View File

@@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct SetMultisampleStateCommand : IGALCommand
{
public CommandType CommandType => CommandType.SetMultisampleState;
private MultisampleDescriptor _multisample;
public void Set(MultisampleDescriptor multisample)
{
_multisample = multisample;
}
public static void Run(ref SetMultisampleStateCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.SetMultisampleState(command._multisample);
}
}
}

View File

@@ -184,6 +184,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
public void SetMultisampleState(MultisampleDescriptor multisample)
{
_renderer.New<SetMultisampleStateCommand>().Set(multisample);
_renderer.QueueCommand();
}
public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
{
_renderer.New<SetPatchParametersCommand>().Set(vertices, defaultOuterLevel, defaultInnerLevel);

View File

@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.GAL
{
_renderer = renderer;
Handle = renderer.CreateBuffer(SupportBuffer.RequiredSize);
renderer.Pipeline.ClearBuffer(Handle, 0, SupportBuffer.RequiredSize, 0);
}
private void MarkDirty(int startOffset, int byteSize)

View File

@@ -166,7 +166,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
nameof(ThreedClassState.BlendEnable),
nameof(ThreedClassState.BlendState)),
new StateUpdateCallbackEntry(UpdateLogicOpState, nameof(ThreedClassState.LogicOpState))
new StateUpdateCallbackEntry(UpdateLogicOpState, nameof(ThreedClassState.LogicOpState)),
new StateUpdateCallbackEntry(UpdateMultisampleState,
nameof(ThreedClassState.AlphaToCoverageDitherEnable),
nameof(ThreedClassState.MultisampleControl))
});
}
@@ -1092,6 +1096,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp);
}
/// <summary>
/// Updates multisample state, based on guest state.
/// </summary>
private void UpdateMultisampleState()
{
bool alphaToCoverageEnable = (_state.State.MultisampleControl & 1) != 0;
bool alphaToOneEnable = (_state.State.MultisampleControl & 0x10) != 0;
_context.Renderer.Pipeline.SetMultisampleState(new MultisampleDescriptor(
alphaToCoverageEnable,
_state.State.AlphaToCoverageDitherEnable,
alphaToOneEnable));
}
/// <summary>
/// Updates host shaders based on the guest GPU state.
/// </summary>
@@ -1231,7 +1249,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.EarlyZForce,
_drawState.Topology,
_state.State.TessMode,
_state.State.ViewportTransformEnable == 0);
_state.State.ViewportTransformEnable == 0,
(_state.State.MultisampleControl & 1) != 0,
_state.State.AlphaToCoverageDitherEnable);
}
/// <summary>

View File

@@ -767,7 +767,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public SamplerIndex SamplerIndex;
public fixed uint Reserved1238[37];
public Boolean32 DepthTestEnable;
public fixed uint Reserved12D0[5];
public fixed uint Reserved12D0[4];
public Boolean32 AlphaToCoverageDitherEnable;
public Boolean32 BlendIndependent;
public Boolean32 DepthWriteEnable;
public Boolean32 AlphaTestEnable;
@@ -802,9 +803,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public Boolean32 PointSpriteEnable;
public fixed uint Reserved1524[3];
public uint ResetCounter;
public uint Reserved1534;
public Boolean32 MultisampleEnable;
public Boolean32 RtDepthStencilEnable;
public fixed uint Reserved153C[5];
public uint MultisampleControl;
public fixed uint Reserved1540[4];
public GpuVa RenderEnableAddress;
public Condition RenderEnableCondition;
public PoolState SamplerPoolState;

View File

@@ -167,6 +167,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce),
topology,
tessMode,
false,
false,
false);
TransformFeedbackDescriptor[] tfdNew = null;

View File

@@ -67,6 +67,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return MemoryMarshal.Cast<byte, ulong>(_data.Span.Slice((int)address));
}
/// <inheritdoc/>
public bool QueryAlphaToCoverageDitherEnable()
{
return _oldSpecState.GraphicsState.AlphaToCoverageEnable && _oldSpecState.GraphicsState.AlphaToCoverageDitherEnable;
}
/// <inheritdoc/>
public int QueryBindingConstantBuffer(int index)
{

View File

@@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 1;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3424;
private const uint CodeGenVersion = 3069;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -66,6 +66,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return MemoryMarshal.Cast<byte, ulong>(_channel.MemoryManager.GetSpan(address, size));
}
/// <inheritdoc/>
public bool QueryAlphaToCoverageDitherEnable()
{
return _state.GraphicsState.AlphaToCoverageEnable && _state.GraphicsState.AlphaToCoverageDitherEnable;
}
/// <inheritdoc/>
public int QueryBindingConstantBuffer(int index)
{

View File

@@ -30,6 +30,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
public readonly bool ViewportTransformDisable;
/// <summary>
/// Indicates whenever alpha-to-coverage is enabled.
/// </summary>
public readonly bool AlphaToCoverageEnable;
/// <summary>
/// Indicates whenever alpha-to-coverage dithering is enabled.
/// </summary>
public readonly bool AlphaToCoverageDitherEnable;
/// <summary>
/// Creates a new GPU graphics state.
/// </summary>
@@ -37,12 +47,22 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="topology">Primitive topology</param>
/// <param name="tessellationMode">Tessellation mode</param>
/// <param name="viewportTransformDisable">Indicates whenever the viewport transform is disabled</param>
public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode, bool viewportTransformDisable)
/// <param name="alphaToCoverageEnable">Indicates whenever alpha-to-coverage is enabled</param>
/// <param name="alphaToCoverageDitherEnable">Indicates whenever alpha-to-coverage dithering is enabled</param>
public GpuChannelGraphicsState(
bool earlyZForce,
PrimitiveTopology topology,
TessMode tessellationMode,
bool viewportTransformDisable,
bool alphaToCoverageEnable,
bool alphaToCoverageDitherEnable)
{
EarlyZForce = earlyZForce;
Topology = topology;
TessellationMode = tessellationMode;
ViewportTransformDisable = viewportTransformDisable;
AlphaToCoverageEnable = alphaToCoverageEnable;
AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable;
}
}
}

View File

@@ -455,6 +455,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false;
}
bool thisA2cDitherEnable = GraphicsState.AlphaToCoverageEnable && GraphicsState.AlphaToCoverageDitherEnable;
bool otherA2cDitherEnable = graphicsState.AlphaToCoverageEnable && graphicsState.AlphaToCoverageDitherEnable;
if (otherA2cDitherEnable != thisA2cDitherEnable)
{
return false;
}
return Matches(channel, poolState, checkTextures, isCompute: false);
}

View File

@@ -5,19 +5,20 @@ namespace Ryujinx.Graphics.OpenGL
{
static class HwCapabilities
{
private static readonly Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
private static readonly Lazy<bool> _supportsDrawTexture = new Lazy<bool>(() => HasExtension("GL_NV_draw_texture"));
private static readonly Lazy<bool> _supportsFragmentShaderInterlock = new Lazy<bool>(() => HasExtension("GL_ARB_fragment_shader_interlock"));
private static readonly Lazy<bool> _supportsFragmentShaderOrdering = new Lazy<bool>(() => HasExtension("GL_INTEL_fragment_shader_ordering"));
private static readonly Lazy<bool> _supportsImageLoadFormatted = new Lazy<bool>(() => HasExtension("GL_EXT_shader_image_load_formatted"));
private static readonly Lazy<bool> _supportsIndirectParameters = new Lazy<bool>(() => HasExtension("GL_ARB_indirect_parameters"));
private static readonly Lazy<bool> _supportsParallelShaderCompile = new Lazy<bool>(() => HasExtension("GL_ARB_parallel_shader_compile"));
private static readonly Lazy<bool> _supportsPolygonOffsetClamp = new Lazy<bool>(() => HasExtension("GL_EXT_polygon_offset_clamp"));
private static readonly Lazy<bool> _supportsQuads = new Lazy<bool>(SupportsQuadsCheck);
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
private static readonly Lazy<bool> _supportsTextureShadowLod = new Lazy<bool>(() => HasExtension("GL_EXT_texture_shadow_lod"));
private static readonly Lazy<bool> _supportsViewportSwizzle = new Lazy<bool>(() => HasExtension("GL_NV_viewport_swizzle"));
private static readonly Lazy<bool> _supportsAlphaToCoverageDitherControl = new Lazy<bool>(() => HasExtension("GL_NV_alpha_to_coverage_dither_control"));
private static readonly Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
private static readonly Lazy<bool> _supportsDrawTexture = new Lazy<bool>(() => HasExtension("GL_NV_draw_texture"));
private static readonly Lazy<bool> _supportsFragmentShaderInterlock = new Lazy<bool>(() => HasExtension("GL_ARB_fragment_shader_interlock"));
private static readonly Lazy<bool> _supportsFragmentShaderOrdering = new Lazy<bool>(() => HasExtension("GL_INTEL_fragment_shader_ordering"));
private static readonly Lazy<bool> _supportsImageLoadFormatted = new Lazy<bool>(() => HasExtension("GL_EXT_shader_image_load_formatted"));
private static readonly Lazy<bool> _supportsIndirectParameters = new Lazy<bool>(() => HasExtension("GL_ARB_indirect_parameters"));
private static readonly Lazy<bool> _supportsParallelShaderCompile = new Lazy<bool>(() => HasExtension("GL_ARB_parallel_shader_compile"));
private static readonly Lazy<bool> _supportsPolygonOffsetClamp = new Lazy<bool>(() => HasExtension("GL_EXT_polygon_offset_clamp"));
private static readonly Lazy<bool> _supportsQuads = new Lazy<bool>(SupportsQuadsCheck);
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
private static readonly Lazy<bool> _supportsTextureShadowLod = new Lazy<bool>(() => HasExtension("GL_EXT_texture_shadow_lod"));
private static readonly Lazy<bool> _supportsViewportSwizzle = new Lazy<bool>(() => HasExtension("GL_NV_viewport_swizzle"));
private static readonly Lazy<int> _maximumComputeSharedMemorySize = new Lazy<int>(() => GetLimit(All.MaxComputeSharedMemorySize));
private static readonly Lazy<int> _storageBufferOffsetAlignment = new Lazy<int>(() => GetLimit(All.ShaderStorageBufferOffsetAlignment));
@@ -43,19 +44,20 @@ namespace Ryujinx.Graphics.OpenGL
public static bool UsePersistentBufferForFlush => _gpuVendor.Value == GpuVendor.AmdWindows || _gpuVendor.Value == GpuVendor.Nvidia;
public static bool SupportsAstcCompression => _supportsAstcCompression.Value;
public static bool SupportsDrawTexture => _supportsDrawTexture.Value;
public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value;
public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value;
public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value;
public static bool SupportsIndirectParameters => _supportsIndirectParameters.Value;
public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value;
public static bool SupportsPolygonOffsetClamp => _supportsPolygonOffsetClamp.Value;
public static bool SupportsQuads => _supportsQuads.Value;
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
public static bool SupportsTextureShadowLod => _supportsTextureShadowLod.Value;
public static bool SupportsViewportSwizzle => _supportsViewportSwizzle.Value;
public static bool SupportsAlphaToCoverageDitherControl => _supportsAlphaToCoverageDitherControl.Value;
public static bool SupportsAstcCompression => _supportsAstcCompression.Value;
public static bool SupportsDrawTexture => _supportsDrawTexture.Value;
public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value;
public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value;
public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value;
public static bool SupportsIndirectParameters => _supportsIndirectParameters.Value;
public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value;
public static bool SupportsPolygonOffsetClamp => _supportsPolygonOffsetClamp.Value;
public static bool SupportsQuads => _supportsQuads.Value;
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
public static bool SupportsTextureShadowLod => _supportsTextureShadowLod.Value;
public static bool SupportsViewportSwizzle => _supportsViewportSwizzle.Value;
public static bool SupportsMismatchingViewFormat => _gpuVendor.Value != GpuVendor.AmdWindows && _gpuVendor.Value != GpuVendor.IntelWindows;
public static bool SupportsNonConstantTextureOffset => _gpuVendor.Value == GpuVendor.Nvidia;

View File

@@ -918,6 +918,34 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public void SetMultisampleState(MultisampleDescriptor multisample)
{
if (multisample.AlphaToCoverageEnable)
{
GL.Enable(EnableCap.SampleAlphaToCoverage);
if (multisample.AlphaToOneEnable)
{
GL.Enable(EnableCap.SampleAlphaToOne);
}
else
{
GL.Disable(EnableCap.SampleAlphaToOne);
}
if (HwCapabilities.SupportsAlphaToCoverageDitherControl)
{
GL.NV.AlphaToCoverageDitherControl(multisample.AlphaToCoverageDitherEnable
? NvAlphaToCoverageDitherControl.AlphaToCoverageDitherEnableNv
: NvAlphaToCoverageDitherControl.AlphaToCoverageDitherDisableNv);
}
}
else
{
GL.Disable(EnableCap.SampleAlphaToCoverage);
}
}
public void SetLineParameters(float width, bool smooth)
{
if (smooth)

View File

@@ -615,7 +615,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
{
bool needsSupportBlock = stage == ShaderStage.Fragment ||
bool needsSupportBlock = stage == ShaderStage.Fragment ||
(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable());
if (!needsSupportBlock && scaleElements == 0)

View File

@@ -34,6 +34,15 @@ namespace Ryujinx.Graphics.Shader
/// <returns>Span of the memory location</returns>
ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize);
/// <summary>
/// Queries whenever the alpha-to-coverage dithering feature is enabled.
/// </summary>
/// <returns>True if the feature is enabled, false otherwise</returns>
bool QueryAlphaToCoverageDitherEnable()
{
return false;
}
/// <summary>
/// Queries the binding number of a constant buffer.
/// </summary>

View File

@@ -205,6 +205,8 @@ namespace Ryujinx.Graphics.Shader.Translation
}
else if (Config.Stage == ShaderStage.Fragment)
{
GenerateAlphaToCoverageDitherDiscard();
if (Config.OmapDepth)
{
Operand dest = Attribute(AttributeConsts.FragmentOutputDepth);
@@ -266,6 +268,35 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
private void GenerateAlphaToCoverageDitherDiscard()
{
// If the feature is disabled, or alpha is not written, then we're done.
if (!Config.GpuAccessor.QueryAlphaToCoverageDitherEnable() || (Config.OmapTargets & 8) == 0)
{
return;
}
// 11 11 11 10 10 10 10 00
// 11 01 01 01 01 00 00 00
Operand ditherMask = Const(unchecked((int)0xfbb99110u));
Operand x = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionX)), Const(1));
Operand y = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionY)), Const(1));
Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1)));
Operand alpha = Register(3, RegisterType.Gpr);
Operand scaledAlpha = this.FPMultiply(this.FPSaturate(alpha), ConstF(8));
Operand quantizedAlpha = this.IMinimumU32(this.FP32ConvertToU32(scaledAlpha), Const(7));
Operand shift = this.BitwiseOr(this.ShiftLeft(quantizedAlpha, Const(2)), xy);
Operand opaque = this.BitwiseAnd(this.ShiftRightU32(ditherMask, shift), Const(1));
Operand a2cDitherEndLabel = Label();
this.BranchIfTrue(a2cDitherEndLabel, opaque);
this.Discard();
this.MarkLabel(a2cDitherEndLabel);
}
public Operation[] GetOperations()
{
return _operations.ToArray();

View File

@@ -217,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// CreateManagedDisplayLayer() -> u64
public ResultCode CreateManagedDisplayLayer(ServiceCtx context)
{
context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long layerId);
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, _pid);
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
context.ResponseData.Write(layerId);
@@ -238,8 +238,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// CreateManagedDisplaySeparableLayer() -> (u64, u64)
public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context)
{
context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long displayLayerId);
context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long recordingLayerId);
context.Device.System.SurfaceFlinger.CreateLayer(out long displayLayerId, _pid);
context.Device.System.SurfaceFlinger.CreateLayer(out long recordingLayerId, _pid);
context.Device.System.SurfaceFlinger.SetRenderLayer(displayLayerId);
context.ResponseData.Write(displayLayerId);

View File

@@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
enum LayerState
{
NotInitialized,
ManagedClosed,
ManagedOpened,
Stray
}
}

View File

@@ -11,6 +11,8 @@ using System.Threading;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
using ResultCode = Ryujinx.HLE.HOS.Services.Vi.ResultCode;
class SurfaceFlinger : IConsumerListener, IDisposable
{
private const int TargetFps = 60;
@@ -45,6 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public BufferItemConsumer Consumer;
public BufferQueueCore Core;
public ulong Owner;
public LayerState State;
}
private class TextureCallbackInformation
@@ -92,24 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
}
}
public IGraphicBufferProducer OpenLayer(ulong pid, long layerId)
{
bool needCreate;
lock (Lock)
{
needCreate = GetLayerByIdLocked(layerId) == null;
}
if (needCreate)
{
CreateLayerFromId(pid, layerId);
}
return GetProducerByLayerId(layerId);
}
public IGraphicBufferProducer CreateLayer(ulong pid, out long layerId)
public IGraphicBufferProducer CreateLayer(out long layerId, ulong pid, LayerState initialState = LayerState.ManagedClosed)
{
layerId = 1;
@@ -124,12 +110,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
}
}
CreateLayerFromId(pid, layerId);
CreateLayerFromId(pid, layerId, initialState);
return GetProducerByLayerId(layerId);
}
private void CreateLayerFromId(ulong pid, long layerId)
private void CreateLayerFromId(ulong pid, long layerId, LayerState initialState)
{
lock (Lock)
{
@@ -148,39 +134,129 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
Producer = producer,
Consumer = new BufferItemConsumer(_device, consumer, 0, -1, false, this),
Core = core,
Owner = pid
Owner = pid,
State = initialState
});
}
}
public bool CloseLayer(long layerId)
public ResultCode OpenLayer(ulong pid, long layerId, out IBinder producer)
{
Layer layer = GetLayerByIdLocked(layerId);
if (layer == null || layer.State != LayerState.ManagedClosed)
{
producer = null;
return ResultCode.InvalidArguments;
}
layer.State = LayerState.ManagedOpened;
producer = layer.Producer;
return ResultCode.Success;
}
public ResultCode CloseLayer(long layerId)
{
lock (Lock)
{
Layer layer = GetLayerByIdLocked(layerId);
if (layer != null)
if (layer == null)
{
HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to close layer {layerId}");
return ResultCode.InvalidValue;
}
bool removed = _layers.Remove(layerId);
CloseLayer(layerId, layer);
// If the layer was removed and the current in use, we need to change the current layer in use.
if (removed && RenderLayerId == layerId)
return ResultCode.Success;
}
}
public ResultCode DestroyManagedLayer(long layerId)
{
lock (Lock)
{
Layer layer = GetLayerByIdLocked(layerId);
if (layer == null)
{
// If no layer is availaible, reset to default value.
if (_layers.Count == 0)
{
SetRenderLayer(0);
}
else
{
SetRenderLayer(_layers.Last().Key);
}
Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy managed layer {layerId} (not found)");
return ResultCode.InvalidValue;
}
return removed;
if (layer.State != LayerState.ManagedClosed && layer.State != LayerState.ManagedOpened)
{
Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy managed layer {layerId} (permission denied)");
return ResultCode.PermissionDenied;
}
HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
if (_layers.Remove(layerId) && layer.State == LayerState.ManagedOpened)
{
CloseLayer(layerId, layer);
}
return ResultCode.Success;
}
}
public ResultCode DestroyStrayLayer(long layerId)
{
lock (Lock)
{
Layer layer = GetLayerByIdLocked(layerId);
if (layer == null)
{
Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy stray layer {layerId} (not found)");
return ResultCode.InvalidValue;
}
if (layer.State != LayerState.Stray)
{
Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy stray layer {layerId} (permission denied)");
return ResultCode.PermissionDenied;
}
HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
if (_layers.Remove(layerId))
{
CloseLayer(layerId, layer);
}
return ResultCode.Success;
}
}
private void CloseLayer(long layerId, Layer layer)
{
// If the layer was removed and the current in use, we need to change the current layer in use.
if (RenderLayerId == layerId)
{
// If no layer is availaible, reset to default value.
if (_layers.Count == 0)
{
SetRenderLayer(0);
}
else
{
SetRenderLayer(_layers.Last().Key);
}
}
if (layer.State == LayerState.ManagedOpened)
{
layer.State = LayerState.ManagedClosed;
}
}

View File

@@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId);
context.Device.System.SurfaceFlinger.CreateLayer(pid, out long layerId);
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid);
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
context.ResponseData.Write(layerId);
@@ -49,9 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
{
long layerId = context.RequestData.ReadInt64();
context.Device.System.SurfaceFlinger.CloseLayer(layerId);
return ResultCode.Success;
return context.Device.System.SurfaceFlinger.DestroyManagedLayer(layerId);
}
[CommandHipc(2012)] // 7.0.0+

View File

@@ -237,7 +237,12 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
long userId = context.RequestData.ReadInt64();
ulong parcelPtr = context.Request.ReceiveBuff[0].Position;
IBinder producer = context.Device.System.SurfaceFlinger.OpenLayer(context.Request.HandleDesc.PId, layerId);
ResultCode result = context.Device.System.SurfaceFlinger.OpenLayer(context.Request.HandleDesc.PId, layerId, out IBinder producer);
if (result != ResultCode.Success)
{
return result;
}
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
@@ -260,9 +265,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
{
long layerId = context.RequestData.ReadInt64();
context.Device.System.SurfaceFlinger.CloseLayer(layerId);
return ResultCode.Success;
return context.Device.System.SurfaceFlinger.CloseLayer(layerId);
}
[CommandHipc(2030)]
@@ -275,7 +278,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
ulong parcelPtr = context.Request.ReceiveBuff[0].Position;
// TODO: support multi display.
IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(0, out long layerId);
IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, 0, LayerState.Stray);
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
@@ -299,9 +302,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
{
long layerId = context.RequestData.ReadInt64();
context.Device.System.SurfaceFlinger.CloseLayer(layerId);
return ResultCode.Success;
return context.Device.System.SurfaceFlinger.DestroyStrayLayer(layerId);
}
[CommandHipc(2101)]

View File

@@ -825,6 +825,14 @@ namespace Ryujinx.Tests.Cpu
};
}
private static uint[] _F_Cvt_S_DH_()
{
return new uint[]
{
0x1E63C020u // FCVT H0, D1
};
}
private static uint[] _F_Cvt_S_HS_()
{
return new uint[]
@@ -833,6 +841,14 @@ namespace Ryujinx.Tests.Cpu
};
}
private static uint[] _F_Cvt_S_HD_()
{
return new uint[]
{
0x1EE2C020u // FCVT D0, H1
};
}
private static uint[] _F_Cvt_ANZ_SU_S_S_()
{
return new uint[]
@@ -1998,6 +2014,22 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Test, Pairwise] [Explicit]
public void F_Cvt_S_DH([ValueSource("_F_Cvt_S_DH_")] uint opcodes,
[ValueSource("_1D_F_")] ulong a,
[Values(RMode.Rn)] RMode rMode)
{
ulong z = TestContext.CurrentContext.Random.NextULong();
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0(a);
int fpcr = (int)rMode << (int)Fpcr.RMode;
SingleOpcode(opcodes, v0: v0, v1: v1, fpcr: fpcr);
CompareAgainstUnicorn();
}
[Test, Pairwise] [Explicit]
public void F_Cvt_S_HS([ValueSource("_F_Cvt_S_HS_")] uint opcodes,
[ValueSource("_1H_F_")] ulong a)
@@ -2011,6 +2043,19 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Test, Pairwise] [Explicit]
public void F_Cvt_S_HD([ValueSource("_F_Cvt_S_HD_")] uint opcodes,
[ValueSource("_1H_F_")] ulong a)
{
ulong z = TestContext.CurrentContext.Random.NextULong();
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0(a);
SingleOpcode(opcodes, v0: v0, v1: v1);
CompareAgainstUnicorn();
}
[Test, Pairwise] [Explicit]
public void F_Cvt_ANZ_SU_S_S([ValueSource("_F_Cvt_ANZ_SU_S_S_")] uint opcodes,
[ValueSource("_1S_F_W_")] ulong a)