Compare commits

..

6 Commits

Author SHA1 Message Date
4dd77316f7 Use vector transform feedback outputs with fragment shaders (#4708)
* Use vector transform feedback outputs with fragment shaders

* Shader cache version bump

* Fix missing outputs when vector transform feedback outputs are used
2023-04-24 08:34:38 +02:00
3f98369a17 Set the console title for GTK again (#4706)
Fixes a regression from #3707 where I accidentally removed that line.
2023-04-24 08:15:19 +02:00
c26aeefe03 Fix amiibo timeout issues & log errors/exceptions (#4712) 2023-04-24 02:08:31 +00:00
666e05f5cb Reducing Memory Allocations 202303 (#4624)
* use ArrayPool, avoid 6000-7000 allocs/sec of runtime

* use ArrayPool, avoid ~7k allocs/second during game execution

* use ArrayPool, avoid ~3000 allocs/sec during game execution

* use MemoryPool, reduce 0.5 MB/sec of new allocations during game execution

* avoid over-allocation by setting List<> Capacity when known

* remove LINQ in KTimeManager.UnscheduleFutureInvocation

* KTimeManager - avoid spinning one more time when the time has arrived

* KTimeManager - let SpinWait decide when to Thread.Yield(), and don't SpinOnce() immediately after Thread.Yield()

* use MemoryPool, reduce ~175k bytes/sec allocation during game execution

* IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array

* Make ButtonMappingEntry a record struct to avoid allocations. Set the List<ButtonMappingEntry> capacity according to use.

* add MemoryBuffer type for working with MemoryPool<byte>

* update changes to use MemoryBuffer

* make parameter ReadOnlySpan instead of Span

* whitespace fix

* Revert "IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array"

This reverts commit f2c698bdf65f049e8481c9f2ec7138d9b9a8261d.

* tweak KTimeManager spin behavior

* replace MemoryBuffer with ByteMemoryPool modeled after System.Buffers.ArrayMemoryPool<T>

* make ByteMemoryPoolBuffer responsible for renting memory
2023-04-24 02:06:23 +00:00
8d9d508dc7 Shader: Bias textureGather instructions on AMD/Intel (#4703)
* Experimental (GLSL, forced)

* SPIR-V attempt

* Add capability

* Fix pCount == 1 on glsl

* Fix typo
2023-04-22 18:02:39 -03:00
e27f5522e2 Removed MotionInput Calibration (#4705)
Don't know why this is here.
It just seems to set the filter to an identity. Which then quickly returns to where its supposed to be anyways.
2023-04-22 15:31:28 +02:00
34 changed files with 461 additions and 182 deletions

View File

@ -11,6 +11,7 @@ using Ryujinx.Audio.Renderer.Server.Voice;
using Ryujinx.Audio.Renderer.Utils; using Ryujinx.Audio.Renderer.Utils;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -149,12 +150,16 @@ namespace Ryujinx.Audio.Renderer.Server
state.InUse = false; state.InUse = false;
} }
Memory<VoiceUpdateState>[] voiceUpdateStatesArray = ArrayPool<Memory<VoiceUpdateState>>.Shared.Rent(Constants.VoiceChannelCountMax);
Span<Memory<VoiceUpdateState>> voiceUpdateStates = voiceUpdateStatesArray.AsSpan(0, Constants.VoiceChannelCountMax);
// Start processing // Start processing
for (int i = 0; i < context.GetCount(); i++) for (int i = 0; i < context.GetCount(); i++)
{ {
VoiceInParameter parameter = parameters[i]; VoiceInParameter parameter = parameters[i];
Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax]; voiceUpdateStates.Fill(Memory<VoiceUpdateState>.Empty);
ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0]; ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0];
@ -197,6 +202,8 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
ArrayPool<Memory<VoiceUpdateState>>.Shared.Return(voiceUpdateStatesArray);
int currentOutputSize = _output.Length; int currentOutputSize = _output.Length;
OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf<VoiceOutStatus>() * context.GetCount()); OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf<VoiceOutStatus>() * context.GetCount());

View File

@ -378,7 +378,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <param name="outStatus">The given user output.</param> /// <param name="outStatus">The given user output.</param>
/// <param name="parameter">The user parameter.</param> /// <param name="parameter">The user parameter.</param>
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param> /// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates) public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates)
{ {
#if DEBUG #if DEBUG
// Sanity check in debug mode of the internal state // Sanity check in debug mode of the internal state
@ -424,7 +424,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param> /// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
/// <param name="mapper">The mapper to use.</param> /// <param name="mapper">The mapper to use.</param>
/// <param name="behaviourContext">The behaviour context.</param> /// <param name="behaviourContext">The behaviour context.</param>
public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext) public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
{ {
errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2]; errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];

View File

@ -7,6 +7,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Models.Amiibo; using Ryujinx.Ui.Common.Models.Amiibo;
using System; using System;
@ -42,13 +43,18 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _showAllAmiibo; private bool _showAllAmiibo;
private bool _useRandomUuid; private bool _useRandomUuid;
private string _usage; private string _usage;
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId) public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
{ {
_owner = owner; _owner = owner;
_httpClient = new HttpClient { Timeout = TimeSpan.FromMilliseconds(5000) };
_httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
};
LastScannedAmiiboId = lastScannedAmiiboId; LastScannedAmiiboId = lastScannedAmiiboId;
TitleId = titleId; TitleId = titleId;
@ -89,9 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_showAllAmiibo = value; _showAllAmiibo = value;
#pragma warning disable 4014
ParseAmiiboData(); ParseAmiiboData();
#pragma warning restore 4014
OnPropertyChanged(); OnPropertyChanged();
} }
@ -203,8 +207,10 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
amiiboJsonString = await DownloadAmiiboJson(); amiiboJsonString = await DownloadAmiiboJson();
} }
catch catch (Exception ex)
{ {
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}");
ShowInfoDialog(); ShowInfoDialog();
} }
} }
@ -369,8 +375,10 @@ namespace Ryujinx.Ava.UI.ViewModels
return false; return false;
} }
catch catch (Exception ex)
{ {
Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}");
ShowInfoDialog(); ShowInfoDialog();
return false; return false;
@ -393,6 +401,8 @@ namespace Ryujinx.Ava.UI.ViewModels
return amiiboJsonString; return amiiboJsonString;
} }
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle], await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage], LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
LocaleManager.Instance[LocaleKeys.InputDialogOk], LocaleManager.Instance[LocaleKeys.InputDialogOk],
@ -429,6 +439,10 @@ namespace Ryujinx.Ava.UI.ViewModels
AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight)); AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
} }
} }
else
{
Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}");
}
} }
private void ResetAmiiboPreview() private void ResetAmiiboPreview()

View File

@ -0,0 +1,51 @@
using System;
using System.Buffers;
using System.Threading;
namespace Ryujinx.Common.Memory
{
public sealed partial class ByteMemoryPool
{
/// <summary>
/// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
/// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/>
/// with a length of the requested size.
/// </summary>
private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte>
{
private byte[] _array;
private readonly int _length;
public ByteMemoryPoolBuffer(int length)
{
_array = ArrayPool<byte>.Shared.Rent(length);
_length = length;
}
/// <summary>
/// Returns a <see cref="Memory{Byte}"/> belonging to this owner.
/// </summary>
public Memory<byte> Memory
{
get
{
byte[] array = _array;
ObjectDisposedException.ThrowIf(array is null, this);
return new Memory<byte>(array, 0, _length);
}
}
public void Dispose()
{
var array = Interlocked.Exchange(ref _array, null);
if (array != null)
{
ArrayPool<byte>.Shared.Return(array);
}
}
}
}
}

View File

@ -0,0 +1,108 @@
using System;
using System.Buffers;
namespace Ryujinx.Common.Memory
{
/// <summary>
/// Provides a pool of re-usable byte array instances.
/// </summary>
public sealed partial class ByteMemoryPool
{
private static readonly ByteMemoryPool _shared = new ByteMemoryPool();
/// <summary>
/// Constructs a <see cref="ByteMemoryPool"/> instance. Private to force access through
/// the <see cref="ByteMemoryPool.Shared"/> instance.
/// </summary>
private ByteMemoryPool()
{
// No implementation
}
/// <summary>
/// Retrieves a shared <see cref="ByteMemoryPool"/> instance.
/// </summary>
public static ByteMemoryPool Shared => _shared;
/// <summary>
/// Returns the maximum buffer size supported by this pool.
/// </summary>
public int MaxBufferSize => Array.MaxLength;
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public IMemoryOwner<byte> Rent(long length)
=> RentImpl(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public IMemoryOwner<byte> Rent(ulong length)
=> RentImpl(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public IMemoryOwner<byte> Rent(int length)
=> RentImpl(length);
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public IMemoryOwner<byte> RentCleared(long length)
=> RentCleared(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public IMemoryOwner<byte> RentCleared(ulong length)
=> RentCleared(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public IMemoryOwner<byte> RentCleared(int length)
{
var buffer = RentImpl(length);
buffer.Memory.Span.Clear();
return buffer;
}
private static ByteMemoryPoolBuffer RentImpl(int length)
{
if ((uint)length > Array.MaxLength)
{
throw new ArgumentOutOfRangeException(nameof(length), length, null);
}
return new ByteMemoryPoolBuffer(length);
}
}
}

View File

@ -48,6 +48,8 @@ namespace Ryujinx.Graphics.GAL
public readonly float MaximumSupportedAnisotropy; public readonly float MaximumSupportedAnisotropy;
public readonly int StorageBufferOffsetAlignment; public readonly int StorageBufferOffsetAlignment;
public readonly int GatherBiasPrecision;
public Capabilities( public Capabilities(
TargetApi api, TargetApi api,
string vendorName, string vendorName,
@ -87,7 +89,8 @@ namespace Ryujinx.Graphics.GAL
uint maximumImagesPerStage, uint maximumImagesPerStage,
int maximumComputeSharedMemorySize, int maximumComputeSharedMemorySize,
float maximumSupportedAnisotropy, float maximumSupportedAnisotropy,
int storageBufferOffsetAlignment) int storageBufferOffsetAlignment,
int gatherBiasPrecision)
{ {
Api = api; Api = api;
VendorName = vendorName; VendorName = vendorName;
@ -128,6 +131,7 @@ namespace Ryujinx.Graphics.GAL
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
MaximumSupportedAnisotropy = maximumSupportedAnisotropy; MaximumSupportedAnisotropy = maximumSupportedAnisotropy;
StorageBufferOffsetAlignment = storageBufferOffsetAlignment; StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
GatherBiasPrecision = gatherBiasPrecision;
} }
} }
} }

View File

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

View File

@ -112,6 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
}; };
} }
public int QueryHostGatherBiasPrecision() => _context.Capabilities.GatherBiasPrecision;
public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision; public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision;
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug; public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;

View File

@ -449,7 +449,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (translatorContexts[i] != null) if (translatorContexts[i] != null)
{ {
translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute); translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute);
translatorContexts[i].SetLastInVertexPipeline(translatorContexts[5] != null); translatorContexts[i].SetLastInVertexPipeline();
break; break;
} }
} }

View File

@ -103,11 +103,14 @@ namespace Ryujinx.Graphics.OpenGL
public Capabilities GetCapabilities() public Capabilities GetCapabilities()
{ {
bool intelWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows;
bool amdWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows;
return new Capabilities( return new Capabilities(
api: TargetApi.OpenGL, api: TargetApi.OpenGL,
vendorName: GpuVendor, vendorName: GpuVendor,
hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows, hasFrontFacingBug: intelWindows,
hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows, hasVectorIndexingBug: amdWindows,
needsFragmentOutputSpecialization: false, needsFragmentOutputSpecialization: false,
reduceShaderPrecision: false, reduceShaderPrecision: false,
supportsAstcCompression: HwCapabilities.SupportsAstcCompression, supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
@ -142,7 +145,8 @@ namespace Ryujinx.Graphics.OpenGL
maximumImagesPerStage: 8, maximumImagesPerStage: 8,
maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize, maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize,
maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy, maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy,
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment); storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan.
} }
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data) public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)

View File

@ -569,7 +569,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (context.Config.TransformFeedbackEnabled && context.Config.Stage == ShaderStage.Fragment) if (context.Config.TransformFeedbackEnabled && context.Config.Stage == ShaderStage.Fragment)
{ {
for (int c = 0; c < 4; c++) int attrOffset = AttributeConsts.UserAttributeBase + attr * 16;
int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset);
if (components > 1)
{
string type = components switch
{
2 => "vec2",
3 => "vec3",
4 => "vec4",
_ => "float"
};
context.AppendLine($"layout (location = {attr}) in {type} {name};");
}
for (int c = components > 1 ? components : 0; c < 4; c++)
{ {
char swzMask = "xyzw"[c]; char swzMask = "xyzw"[c];
@ -642,7 +658,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
{ {
int attrOffset = AttributeConsts.UserAttributeBase + attr * 16; int attrOffset = AttributeConsts.UserAttributeBase + attr * 16;
int components = context.Config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1; int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset);
if (components > 1) if (components > 1)
{ {
@ -664,22 +680,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};"); context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};");
} }
else
for (int c = components > 1 ? components : 0; c < 4; c++)
{ {
for (int c = 0; c < 4; c++) char swzMask = "xyzw"[c];
string xfb = string.Empty;
var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4);
if (tfOutput.Valid)
{ {
char swzMask = "xyzw"[c]; xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
string xfb = string.Empty;
var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4);
if (tfOutput.Valid)
{
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
}
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};");
} }
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};");
} }
} }
else else

View File

@ -677,7 +677,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return vector; return vector;
} }
Append(ApplyScaling(AssemblePVector(pCount))); string ApplyBias(string vector)
{
int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision();
if (isGather && gatherBiasPrecision != 0)
{
// GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels.
// Offset by the gather precision divided by 2 to correct for rounding.
if (pCount == 1)
{
vector = $"{vector} + (1.0 / (float(textureSize({samplerName}, 0)) * float({1 << (gatherBiasPrecision + 1)})))";
}
else
{
vector = $"{vector} + (1.0 / (vec{pCount}(textureSize({samplerName}, 0).{"xyz".Substring(0, pCount)}) * float({1 << (gatherBiasPrecision + 1)})))";
}
}
return vector;
}
Append(ApplyBias(ApplyScaling(AssemblePVector(pCount))));
string AssembleDerivativesVector(int count) string AssembleDerivativesVector(int count)
{ {

View File

@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
((config.LastInVertexPipeline && isOutAttr) || ((config.LastInVertexPipeline && isOutAttr) ||
(config.Stage == ShaderStage.Fragment && !isOutAttr))) (config.Stage == ShaderStage.Fragment && !isOutAttr)))
{ {
int components = config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1; int components = context.Info.GetTransformFeedbackOutputComponents(attrOffset);
string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}"; string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}";
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))

View File

@ -341,7 +341,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
attrOffset = attr; attrOffset = attr;
type = elemType; type = elemType;
if (Config.LastInPipeline && isOutAttr) if (isOutAttr)
{ {
int components = Info.GetTransformFeedbackOutputComponents(attr); int components = Info.GetTransformFeedbackOutputComponents(attr);

View File

@ -673,7 +673,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
int components = 1; int components = 1;
var type = attrInfo.Type & AggregateType.ElementTypeMask; var type = attrInfo.Type & AggregateType.ElementTypeMask;
if (context.Config.LastInPipeline && isOutAttr) if (isOutAttr)
{ {
components = context.Info.GetTransformFeedbackOutputComponents(attr); components = context.Info.GetTransformFeedbackOutputComponents(attr);

View File

@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Numerics; using System.Numerics;
using static Spv.Specification; using static Spv.Specification;
@ -1556,6 +1557,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
} }
SpvInstruction ApplyBias(SpvInstruction vector, SpvInstruction image)
{
int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision();
if (isGather && gatherBiasPrecision != 0)
{
// GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels.
// Offset by the gather precision divided by 2 to correct for rounding.
var sizeType = pCount == 1 ? context.TypeS32() : context.TypeVector(context.TypeS32(), pCount);
var pVectorType = pCount == 1 ? context.TypeFP32() : context.TypeVector(context.TypeFP32(), pCount);
var bias = context.Constant(context.TypeFP32(), (float)(1 << (gatherBiasPrecision + 1)));
var biasVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(bias, pCount).ToArray());
var one = context.Constant(context.TypeFP32(), 1f);
var oneVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(one, pCount).ToArray());
var divisor = context.FMul(
pVectorType,
context.ConvertSToF(pVectorType, context.ImageQuerySize(sizeType, image)),
biasVector);
vector = context.FAdd(pVectorType, vector, context.FDiv(pVectorType, oneVector, divisor));
}
return vector;
}
SpvInstruction pCoords = AssemblePVector(pCount); SpvInstruction pCoords = AssemblePVector(pCount);
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount); pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount);
@ -1716,6 +1744,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
image = context.Image(imageType, image); image = context.Image(imageType, image);
} }
pCoords = ApplyBias(pCoords, image);
var operands = operandsList.ToArray(); var operands = operandsList.ToArray();
SpvInstruction result; SpvInstruction result;

View File

@ -196,6 +196,15 @@ namespace Ryujinx.Graphics.Shader
return false; return false;
} }
/// <summary>
/// Queries host's gather operation precision bits for biasing their coordinates. Zero means no bias.
/// </summary>
/// <returns>Bits of gather operation precision to use for coordinate bias</returns>
int QueryHostGatherBiasPrecision()
{
return 0;
}
/// <summary> /// <summary>
/// Queries host about whether to reduce precision to improve performance. /// Queries host about whether to reduce precision to improve performance.
/// </summary> /// </summary>

View File

@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
context.LeaveFunction(); context.LeaveFunction();
} }
if (config.TransformFeedbackEnabled && config.LastInVertexPipeline) if (config.TransformFeedbackEnabled && (config.LastInVertexPipeline || config.Stage == ShaderStage.Fragment))
{ {
for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++) for (int tfbIndex = 0; tfbIndex < 4; tfbIndex++)
{ {

View File

@ -17,7 +17,6 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderStage Stage { get; } public ShaderStage Stage { get; }
public bool GpPassthrough { get; } public bool GpPassthrough { get; }
public bool LastInPipeline { get; private set; }
public bool LastInVertexPipeline { get; private set; } public bool LastInVertexPipeline { get; private set; }
public bool HasLayerInputAttribute { get; private set; } public bool HasLayerInputAttribute { get; private set; }
@ -145,7 +144,6 @@ namespace Ryujinx.Graphics.Shader.Translation
OmapSampleMask = header.OmapSampleMask; OmapSampleMask = header.OmapSampleMask;
OmapDepth = header.OmapDepth; OmapDepth = header.OmapDepth;
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
LastInPipeline = true;
LastInVertexPipeline = header.Stage < ShaderStage.Fragment; LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
} }
@ -253,13 +251,8 @@ namespace Ryujinx.Graphics.Shader.Translation
GpLayerInputAttribute = attr; GpLayerInputAttribute = attr;
} }
public void SetLastInVertexPipeline(bool hasFragment) public void SetLastInVertexPipeline()
{ {
if (!hasFragment)
{
LastInPipeline = true;
}
LastInVertexPipeline = true; LastInVertexPipeline = true;
} }
@ -331,8 +324,6 @@ namespace Ryujinx.Graphics.Shader.Translation
config._perPatchAttributeLocations = locationsMap; config._perPatchAttributeLocations = locationsMap;
} }
LastInPipeline = false;
// We don't consider geometry shaders using the geometry shader passthrough feature // We don't consider geometry shaders using the geometry shader passthrough feature
// as being the last because when this feature is used, it can't actually modify any of the outputs, // as being the last because when this feature is used, it can't actually modify any of the outputs,
// so the stage that comes before it is the last one that can do modifications. // so the stage that comes before it is the last one that can do modifications.

View File

@ -143,9 +143,9 @@ namespace Ryujinx.Graphics.Shader.Translation
_config.SetGeometryShaderLayerInputAttribute(attr); _config.SetGeometryShaderLayerInputAttribute(attr);
} }
public void SetLastInVertexPipeline(bool hasFragment) public void SetLastInVertexPipeline()
{ {
_config.SetLastInVertexPipeline(hasFragment); _config.SetLastInVertexPipeline();
} }
public ShaderProgram Translate(TranslatorContext other = null) public ShaderProgram Translate(TranslatorContext other = null)

View File

@ -46,6 +46,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly SampleCountFlags SupportedSampleCounts; public readonly SampleCountFlags SupportedSampleCounts;
public readonly PortabilitySubsetFlags PortabilitySubset; public readonly PortabilitySubsetFlags PortabilitySubset;
public readonly uint VertexBufferAlignment; public readonly uint VertexBufferAlignment;
public readonly uint SubTexelPrecisionBits;
public HardwareCapabilities( public HardwareCapabilities(
bool supportsIndexTypeUint8, bool supportsIndexTypeUint8,
@ -77,7 +78,8 @@ namespace Ryujinx.Graphics.Vulkan
ShaderStageFlags requiredSubgroupSizeStages, ShaderStageFlags requiredSubgroupSizeStages,
SampleCountFlags supportedSampleCounts, SampleCountFlags supportedSampleCounts,
PortabilitySubsetFlags portabilitySubset, PortabilitySubsetFlags portabilitySubset,
uint vertexBufferAlignment) uint vertexBufferAlignment,
uint subTexelPrecisionBits)
{ {
SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsIndexTypeUint8 = supportsIndexTypeUint8;
SupportsCustomBorderColor = supportsCustomBorderColor; SupportsCustomBorderColor = supportsCustomBorderColor;
@ -109,6 +111,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportedSampleCounts = supportedSampleCounts; SupportedSampleCounts = supportedSampleCounts;
PortabilitySubset = portabilitySubset; PortabilitySubset = portabilitySubset;
VertexBufferAlignment = vertexBufferAlignment; VertexBufferAlignment = vertexBufferAlignment;
SubTexelPrecisionBits = subTexelPrecisionBits;
} }
} }
} }

View File

@ -311,7 +311,8 @@ namespace Ryujinx.Graphics.Vulkan
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
supportedSampleCounts, supportedSampleCounts,
portabilityFlags, portabilityFlags,
vertexBufferAlignment); vertexBufferAlignment,
properties.Limits.SubTexelPrecisionBits);
IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice); IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice);
@ -576,7 +577,8 @@ namespace Ryujinx.Graphics.Vulkan
maximumImagesPerStage: Constants.MaxImagesPerStage, maximumImagesPerStage: Constants.MaxImagesPerStage,
maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize, maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize,
maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy, maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy,
storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment); storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0);
} }
public HardwareInfo GetHardwareInfo() public HardwareInfo GetHardwareInfo()

View File

@ -27,98 +27,103 @@ namespace Ryujinx.HLE.HOS.Ipc
public IpcMessage() public IpcMessage()
{ {
PtrBuff = new List<IpcPtrBuffDesc>(); PtrBuff = new List<IpcPtrBuffDesc>(0);
SendBuff = new List<IpcBuffDesc>(); SendBuff = new List<IpcBuffDesc>(0);
ReceiveBuff = new List<IpcBuffDesc>(); ReceiveBuff = new List<IpcBuffDesc>(0);
ExchangeBuff = new List<IpcBuffDesc>(); ExchangeBuff = new List<IpcBuffDesc>(0);
RecvListBuff = new List<IpcRecvListBuffDesc>(); RecvListBuff = new List<IpcRecvListBuffDesc>(0);
ObjectIds = new List<int>(); ObjectIds = new List<int>(0);
} }
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this() public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr)
{ {
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data)) using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
{ {
BinaryReader reader = new BinaryReader(ms); BinaryReader reader = new BinaryReader(ms);
Initialize(reader, cmdPtr); int word0 = reader.ReadInt32();
} int word1 = reader.ReadInt32();
}
private void Initialize(BinaryReader reader, long cmdPtr) Type = (IpcMessageType)(word0 & 0xffff);
{
int word0 = reader.ReadInt32();
int word1 = reader.ReadInt32();
Type = (IpcMessageType)(word0 & 0xffff); int ptrBuffCount = (word0 >> 16) & 0xf;
int sendBuffCount = (word0 >> 20) & 0xf;
int recvBuffCount = (word0 >> 24) & 0xf;
int xchgBuffCount = (word0 >> 28) & 0xf;
int ptrBuffCount = (word0 >> 16) & 0xf; int rawDataSize = (word1 >> 0) & 0x3ff;
int sendBuffCount = (word0 >> 20) & 0xf; int recvListFlags = (word1 >> 10) & 0xf;
int recvBuffCount = (word0 >> 24) & 0xf; bool hndDescEnable = ((word1 >> 31) & 0x1) != 0;
int xchgBuffCount = (word0 >> 28) & 0xf;
int rawDataSize = (word1 >> 0) & 0x3ff; if (hndDescEnable)
int recvListFlags = (word1 >> 10) & 0xf;
bool hndDescEnable = ((word1 >> 31) & 0x1) != 0;
if (hndDescEnable)
{
HandleDesc = new IpcHandleDesc(reader);
}
for (int index = 0; index < ptrBuffCount; index++)
{
PtrBuff.Add(new IpcPtrBuffDesc(reader));
}
void ReadBuff(List<IpcBuffDesc> buff, int count)
{
for (int index = 0; index < count; index++)
{ {
buff.Add(new IpcBuffDesc(reader)); HandleDesc = new IpcHandleDesc(reader);
} }
}
ReadBuff(SendBuff, sendBuffCount); PtrBuff = new List<IpcPtrBuffDesc>(ptrBuffCount);
ReadBuff(ReceiveBuff, recvBuffCount);
ReadBuff(ExchangeBuff, xchgBuffCount);
rawDataSize *= 4; for (int index = 0; index < ptrBuffCount; index++)
{
PtrBuff.Add(new IpcPtrBuffDesc(reader));
}
long recvListPos = reader.BaseStream.Position + rawDataSize; static List<IpcBuffDesc> ReadBuff(BinaryReader reader, int count)
{
List<IpcBuffDesc> buff = new List<IpcBuffDesc>(count);
for (int index = 0; index < count; index++)
{
buff.Add(new IpcBuffDesc(reader));
}
return buff;
}
SendBuff = ReadBuff(reader, sendBuffCount);
ReceiveBuff = ReadBuff(reader, recvBuffCount);
ExchangeBuff = ReadBuff(reader, xchgBuffCount);
rawDataSize *= 4;
long recvListPos = reader.BaseStream.Position + rawDataSize;
// Only CMIF has the padding requirements. // Only CMIF has the padding requirements.
if (Type < IpcMessageType.TipcCloseSession) if (Type < IpcMessageType.TipcCloseSession)
{ {
long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr); long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr);
if (rawDataSize != 0) if (rawDataSize != 0)
{ {
rawDataSize -= (int)pad0; rawDataSize -= (int)pad0;
}
reader.BaseStream.Seek(pad0, SeekOrigin.Current);
} }
reader.BaseStream.Seek(pad0, SeekOrigin.Current); int recvListCount = recvListFlags - 2;
}
int recvListCount = recvListFlags - 2; if (recvListCount == 0)
{
recvListCount = 1;
}
else if (recvListCount < 0)
{
recvListCount = 0;
}
if (recvListCount == 0) RawData = reader.ReadBytes(rawDataSize);
{
recvListCount = 1;
}
else if (recvListCount < 0)
{
recvListCount = 0;
}
RawData = reader.ReadBytes(rawDataSize); reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin);
reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin); RecvListBuff = new List<IpcRecvListBuffDesc>(recvListCount);
for (int index = 0; index < recvListCount; index++) for (int index = 0; index < recvListCount; index++)
{ {
RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64())); RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
}
ObjectIds = new List<int>(0);
} }
} }

View File

@ -71,7 +71,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
lock (_context.CriticalSection.Lock) lock (_context.CriticalSection.Lock)
{ {
_waitingObjects.RemoveAll(x => x.Object == schedulerObj); for (int index = _waitingObjects.Count - 1; index >= 0; index--)
{
if (_waitingObjects[index].Object == schedulerObj)
{
_waitingObjects.RemoveAt(index);
}
}
} }
} }
@ -105,16 +111,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
} }
else else
{ {
while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks <= next.TimePoint) while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks < next.TimePoint)
{ {
// Our time is close - don't let SpinWait go off and potentially Thread.Sleep().
if (spinWait.NextSpinWillYield) if (spinWait.NextSpinWillYield)
{ {
Thread.Yield(); Thread.Yield();
spinWait.Reset(); spinWait.Reset();
} }
else
spinWait.SpinOnce(); {
spinWait.SpinOnce();
}
} }
spinWait.Reset(); spinWait.Reset();

View File

@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using System; using System;
using System.Buffers;
using System.Threading; using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
@ -553,7 +554,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KProcess currentProcess = KernelStatic.GetCurrentProcess(); KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length]; KSynchronizationObject[] syncObjsArray = ArrayPool<KSynchronizationObject>.Shared.Rent(handles.Length);
Span<KSynchronizationObject> syncObjs = syncObjsArray.AsSpan(0, handles.Length);
for (int index = 0; index < handles.Length; index++) for (int index = 0; index < handles.Length; index++)
{ {
@ -606,6 +609,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
} }
} }
ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray);
return result; return result;
} }

View File

@ -1,6 +1,7 @@
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Threading namespace Ryujinx.HLE.HOS.Kernel.Threading
@ -59,7 +60,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
} }
else else
{ {
LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length]; LinkedListNode<KThread>[] syncNodesArray = ArrayPool<LinkedListNode<KThread>>.Shared.Rent(syncObjs.Length);
Span<LinkedListNode<KThread>> syncNodes = syncNodesArray.AsSpan(0, syncObjs.Length);
for (int index = 0; index < syncObjs.Length; index++) for (int index = 0; index < syncObjs.Length; index++)
{ {
@ -101,6 +104,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
handleIndex = index; handleIndex = index;
} }
} }
ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray);
} }
_context.CriticalSection.Leave(); _context.CriticalSection.Leave();

View File

@ -1,4 +1,5 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
@ -68,25 +69,29 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
Memory<byte> output = new byte[outputSize]; using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.Shared.RentCleared(outputSize))
Memory<byte> performanceOutput = new byte[performanceOutputSize]; using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Shared.RentCleared(performanceOutputSize))
using MemoryHandle outputHandle = output.Pin();
using MemoryHandle performanceOutputHandle = performanceOutput.Pin();
ResultCode result = _impl.RequestUpdate(output, performanceOutput, input);
if (result == ResultCode.Success)
{ {
context.Memory.Write(outputPosition, output.Span); Memory<byte> output = outputOwner.Memory;
context.Memory.Write(performanceOutputPosition, performanceOutput.Span); Memory<byte> performanceOutput = performanceOutputOwner.Memory;
}
else
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}");
}
return result; using MemoryHandle outputHandle = output.Pin();
using MemoryHandle performanceOutputHandle = performanceOutput.Pin();
ResultCode result = _impl.RequestUpdate(output, performanceOutput, input);
if (result == ResultCode.Success)
{
context.Memory.Write(outputPosition, output.Span);
context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
}
else
{
Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}");
}
return result;
}
} }
[CommandCmif(5)] [CommandCmif(5)]

View File

@ -76,6 +76,8 @@ namespace Ryujinx.HLE.HOS.Services
context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin); context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin);
context.Request.ObjectIds.EnsureCapacity(inputObjCount);
for (int index = 0; index < inputObjCount; index++) for (int index = 0; index < inputObjCount; index++)
{ {
context.Request.ObjectIds.Add(context.RequestData.ReadInt32()); context.Request.ObjectIds.Add(context.RequestData.ReadInt32());

View File

@ -217,6 +217,8 @@ namespace Ryujinx.HLE.HOS.Services
if (noReceive) if (noReceive)
{ {
response.PtrBuff.EnsureCapacity(request.RecvListBuff.Count);
for (int i = 0; i < request.RecvListBuff.Count; i++) for (int i = 0; i < request.RecvListBuff.Count; i++)
{ {
ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan(sizesOffset + i * 2, 2)); ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan(sizesOffset + i * 2, 2));

View File

@ -1,7 +1,9 @@
using Ryujinx.HLE.HOS.Ipc; using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using System; using System;
using System.Buffers;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{ {
@ -83,16 +85,19 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize); ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize);
Span<byte> outputParcel = new Span<byte>(new byte[replySize]); using (IMemoryOwner<byte> outputParcelOwner = ByteMemoryPool.Shared.RentCleared(replySize))
ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel);
if (result == ResultCode.Success)
{ {
context.Memory.Write(replyPos, outputParcel); Span<byte> outputParcel = outputParcelOwner.Memory.Span;
}
ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel);
return result; if (result == ResultCode.Success)
{
context.Memory.Write(replyPos, outputParcel);
}
return result;
}
} }
protected abstract ResultCode AdjustRefcount(int binderId, int addVal, int type); protected abstract ResultCode AdjustRefcount(int binderId, int addVal, int type);

View File

@ -12,17 +12,7 @@ namespace Ryujinx.Input.SDL2
{ {
private bool HasConfiguration => _configuration != null; private bool HasConfiguration => _configuration != null;
private class ButtonMappingEntry private record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From);
{
public readonly GamepadButtonInputId To;
public readonly GamepadButtonInputId From;
public ButtonMappingEntry(GamepadButtonInputId to, GamepadButtonInputId from)
{
To = to;
From = from;
}
}
private StandardControllerInputConfig _configuration; private StandardControllerInputConfig _configuration;
@ -85,7 +75,7 @@ namespace Ryujinx.Input.SDL2
public SDL2Gamepad(IntPtr gamepadHandle, string driverId) public SDL2Gamepad(IntPtr gamepadHandle, string driverId)
{ {
_gamepadHandle = gamepadHandle; _gamepadHandle = gamepadHandle;
_buttonsUserMapping = new List<ButtonMappingEntry>(); _buttonsUserMapping = new List<ButtonMappingEntry>(20);
Name = SDL_GameControllerName(_gamepadHandle); Name = SDL_GameControllerName(_gamepadHandle);
Id = driverId; Id = driverId;

View File

@ -12,7 +12,6 @@ namespace Ryujinx.Input
public Vector3 Rotation { get; set; } public Vector3 Rotation { get; set; }
private readonly MotionSensorFilter _filter; private readonly MotionSensorFilter _filter;
private int _calibrationFrame = 0;
public MotionInput() public MotionInput()
{ {
@ -29,26 +28,6 @@ namespace Ryujinx.Input
{ {
if (TimeStamp != 0) if (TimeStamp != 0)
{ {
if (gyro.Length() <= 1f && accel.Length() >= 0.8f && accel.Z >= 0.8f)
{
_calibrationFrame++;
if (_calibrationFrame >= 90)
{
gyro = Vector3.Zero;
Rotation = Vector3.Zero;
_filter.Reset();
_calibrationFrame = 0;
}
}
else
{
_calibrationFrame = 0;
}
Accelerometer = -accel; Accelerometer = -accel;
if (gyro.Length() < deadzone) if (gyro.Length() < deadzone)

View File

@ -96,6 +96,8 @@ namespace Ryujinx
// Delete backup files after updating. // Delete backup files after updating.
Task.Run(Updater.CleanupUpdate); Task.Run(Updater.CleanupUpdate);
Console.Title = $"Ryujinx Console {Version}";
// NOTE: GTK3 doesn't init X11 in a multi threaded way. // NOTE: GTK3 doesn't init X11 in a multi threaded way.
// This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads). // This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads).
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())

View File

@ -1,6 +1,7 @@
using Gtk; using Gtk;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Models.Amiibo; using Ryujinx.Ui.Common.Models.Amiibo;
@ -12,7 +13,6 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi; using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi;
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext; using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
@ -57,7 +57,7 @@ namespace Ryujinx.Ui.Windows
_httpClient = new HttpClient() _httpClient = new HttpClient()
{ {
Timeout = TimeSpan.FromMilliseconds(5000) Timeout = TimeSpan.FromSeconds(30)
}; };
Directory.CreateDirectory(System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); Directory.CreateDirectory(System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
@ -93,8 +93,10 @@ namespace Ryujinx.Ui.Windows
{ {
amiiboJsonString = await DownloadAmiiboJson(); amiiboJsonString = await DownloadAmiiboJson();
} }
catch catch (Exception ex)
{ {
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}");
ShowInfoDialog(); ShowInfoDialog();
Close(); Close();
@ -183,8 +185,10 @@ namespace Ryujinx.Ui.Windows
return false; return false;
} }
catch catch (Exception ex)
{ {
Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}");
ShowInfoDialog(); ShowInfoDialog();
return false; return false;
@ -208,6 +212,8 @@ namespace Ryujinx.Ui.Windows
} }
else else
{ {
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
GtkDialog.CreateInfoDialog($"Amiibo API", "An error occured while fetching information from the API."); GtkDialog.CreateInfoDialog($"Amiibo API", "An error occured while fetching information from the API.");
Close(); Close();
@ -233,6 +239,10 @@ namespace Ryujinx.Ui.Windows
_amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, Gdk.InterpType.Bilinear); _amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, Gdk.InterpType.Bilinear);
} }
else
{
Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}");
}
} }
private void ShowInfoDialog() private void ShowInfoDialog()
@ -374,4 +384,4 @@ namespace Ryujinx.Ui.Windows
base.Dispose(disposing); base.Dispose(disposing);
} }
} }
} }