Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a3dc295c5f | ||
|
2ef4f92b07 | ||
|
2b6cc4b353 | ||
|
075575200d | ||
|
3a3b51893e | ||
|
44dbab3848 | ||
|
cada4d04ef | ||
|
e9edf0ab7f | ||
|
6e40b64554 | ||
|
1a676ee913 | ||
|
a23d8cb92f | ||
|
ab12fbe963 |
@@ -36,8 +36,8 @@
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of October 2023, Ryujinx has been tested on approximately 4,200 titles;
|
As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
|
||||||
over 4,150 boot past menus and into gameplay, with roughly 3,500 of those being considered playable.
|
over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
|
||||||
|
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
||||||
|
|
||||||
|
@@ -4,6 +4,8 @@
|
|||||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
|
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
|
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></Policy></s:String>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
@@ -857,8 +857,14 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
threads.ForEach((thread) => thread.Start());
|
foreach (var thread in threads)
|
||||||
threads.ForEach((thread) => thread.Join());
|
{
|
||||||
|
thread.Start();
|
||||||
|
}
|
||||||
|
foreach (var thread in threads)
|
||||||
|
{
|
||||||
|
thread.Join();
|
||||||
|
}
|
||||||
|
|
||||||
threads.Clear();
|
threads.Clear();
|
||||||
|
|
||||||
|
@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public readonly bool SupportsMismatchingViewFormat;
|
public readonly bool SupportsMismatchingViewFormat;
|
||||||
public readonly bool SupportsCubemapView;
|
public readonly bool SupportsCubemapView;
|
||||||
public readonly bool SupportsNonConstantTextureOffset;
|
public readonly bool SupportsNonConstantTextureOffset;
|
||||||
|
public readonly bool SupportsQuads;
|
||||||
public readonly bool SupportsSeparateSampler;
|
public readonly bool SupportsSeparateSampler;
|
||||||
public readonly bool SupportsShaderBallot;
|
public readonly bool SupportsShaderBallot;
|
||||||
public readonly bool SupportsShaderBarrierDivergence;
|
public readonly bool SupportsShaderBarrierDivergence;
|
||||||
@@ -93,6 +94,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
bool supportsMismatchingViewFormat,
|
bool supportsMismatchingViewFormat,
|
||||||
bool supportsCubemapView,
|
bool supportsCubemapView,
|
||||||
bool supportsNonConstantTextureOffset,
|
bool supportsNonConstantTextureOffset,
|
||||||
|
bool supportsQuads,
|
||||||
bool supportsSeparateSampler,
|
bool supportsSeparateSampler,
|
||||||
bool supportsShaderBallot,
|
bool supportsShaderBallot,
|
||||||
bool supportsShaderBarrierDivergence,
|
bool supportsShaderBarrierDivergence,
|
||||||
@@ -146,6 +148,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
|
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
|
||||||
SupportsCubemapView = supportsCubemapView;
|
SupportsCubemapView = supportsCubemapView;
|
||||||
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
||||||
|
SupportsQuads = supportsQuads;
|
||||||
SupportsSeparateSampler = supportsSeparateSampler;
|
SupportsSeparateSampler = supportsSeparateSampler;
|
||||||
SupportsShaderBallot = supportsShaderBallot;
|
SupportsShaderBallot = supportsShaderBallot;
|
||||||
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
||||||
|
@@ -395,8 +395,14 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
{
|
{
|
||||||
Renderer.CreateSync(SyncNumber, strict);
|
Renderer.CreateSync(SyncNumber, strict);
|
||||||
|
|
||||||
SyncActions.ForEach(action => action.SyncPreAction(syncpoint));
|
foreach (var action in SyncActions)
|
||||||
SyncpointActions.ForEach(action => action.SyncPreAction(syncpoint));
|
{
|
||||||
|
action.SyncPreAction(syncpoint);
|
||||||
|
}
|
||||||
|
foreach (var action in SyncpointActions)
|
||||||
|
{
|
||||||
|
action.SyncPreAction(syncpoint);
|
||||||
|
}
|
||||||
|
|
||||||
SyncNumber++;
|
SyncNumber++;
|
||||||
|
|
||||||
|
@@ -390,7 +390,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
_views.Remove(texture);
|
_views.Remove(texture);
|
||||||
|
|
||||||
Group.RemoveView(texture);
|
Group.RemoveView(_views, texture);
|
||||||
|
|
||||||
texture._viewStorage = texture;
|
texture._viewStorage = texture;
|
||||||
|
|
||||||
|
@@ -88,9 +88,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private MultiRange TextureRange => Storage.Range;
|
private MultiRange TextureRange => Storage.Range;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The views list from the storage texture.
|
/// The views array from the storage texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<Texture> _views;
|
private Texture[] _views;
|
||||||
private TextureGroupHandle[] _handles;
|
private TextureGroupHandle[] _handles;
|
||||||
private bool[] _loadNeeded;
|
private bool[] _loadNeeded;
|
||||||
|
|
||||||
@@ -1074,7 +1074,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public void UpdateViews(List<Texture> views, Texture texture)
|
public void UpdateViews(List<Texture> views, Texture texture)
|
||||||
{
|
{
|
||||||
// This is saved to calculate overlapping views for each handle.
|
// This is saved to calculate overlapping views for each handle.
|
||||||
_views = views;
|
_views = views.ToArray();
|
||||||
|
|
||||||
bool layerViews = _hasLayerViews;
|
bool layerViews = _hasLayerViews;
|
||||||
bool mipViews = _hasMipViews;
|
bool mipViews = _hasMipViews;
|
||||||
@@ -1136,9 +1136,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a view from the group, removing it from all overlap lists.
|
/// Removes a view from the group, removing it from all overlap lists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="views">The views list of the storage texture</param>
|
||||||
/// <param name="view">View to remove from the group</param>
|
/// <param name="view">View to remove from the group</param>
|
||||||
public void RemoveView(Texture view)
|
public void RemoveView(List<Texture> views, Texture view)
|
||||||
{
|
{
|
||||||
|
// This is saved to calculate overlapping views for each handle.
|
||||||
|
_views = views.ToArray();
|
||||||
|
|
||||||
int offset = FindOffset(view);
|
int offset = FindOffset(view);
|
||||||
|
|
||||||
foreach (TextureGroupHandle handle in _handles)
|
foreach (TextureGroupHandle handle in _handles)
|
||||||
@@ -1605,9 +1609,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
Storage.SignalModifiedDirty();
|
Storage.SignalModifiedDirty();
|
||||||
|
|
||||||
if (_views != null)
|
Texture[] views = _views;
|
||||||
|
|
||||||
|
if (views != null)
|
||||||
{
|
{
|
||||||
foreach (Texture texture in _views)
|
foreach (Texture texture in views)
|
||||||
{
|
{
|
||||||
texture.SignalModifiedDirty();
|
texture.SignalModifiedDirty();
|
||||||
}
|
}
|
||||||
|
@@ -121,7 +121,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public TextureGroupHandle(TextureGroup group,
|
public TextureGroupHandle(TextureGroup group,
|
||||||
int offset,
|
int offset,
|
||||||
ulong size,
|
ulong size,
|
||||||
List<Texture> views,
|
IEnumerable<Texture> views,
|
||||||
int firstLayer,
|
int firstLayer,
|
||||||
int firstLevel,
|
int firstLevel,
|
||||||
int baseSlice,
|
int baseSlice,
|
||||||
@@ -201,8 +201,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Calculate a list of which views overlap this handle.
|
/// Calculate a list of which views overlap this handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="group">The parent texture group, used to find a view's base CPU VA offset</param>
|
/// <param name="group">The parent texture group, used to find a view's base CPU VA offset</param>
|
||||||
/// <param name="views">The list of views to search for overlaps</param>
|
/// <param name="views">The views to search for overlaps</param>
|
||||||
public void RecalculateOverlaps(TextureGroup group, List<Texture> views)
|
public void RecalculateOverlaps(TextureGroup group, IEnumerable<Texture> views)
|
||||||
{
|
{
|
||||||
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
|
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
|
||||||
lock (Overlaps)
|
lock (Overlaps)
|
||||||
|
@@ -18,6 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private readonly ShaderSpecializationState _newSpecState;
|
private readonly ShaderSpecializationState _newSpecState;
|
||||||
private readonly int _stageIndex;
|
private readonly int _stageIndex;
|
||||||
private readonly bool _isVulkan;
|
private readonly bool _isVulkan;
|
||||||
|
private readonly bool _hasGeometryShader;
|
||||||
|
private readonly bool _supportsQuads;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the cached GPU state accessor for shader translation.
|
/// Creates a new instance of the cached GPU state accessor for shader translation.
|
||||||
@@ -29,6 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
/// <param name="newSpecState">Shader specialization state of the recompiled shader</param>
|
/// <param name="newSpecState">Shader specialization state of the recompiled shader</param>
|
||||||
/// <param name="counts">Resource counts shared across all shader stages</param>
|
/// <param name="counts">Resource counts shared across all shader stages</param>
|
||||||
/// <param name="stageIndex">Shader stage index</param>
|
/// <param name="stageIndex">Shader stage index</param>
|
||||||
|
/// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
|
||||||
public DiskCacheGpuAccessor(
|
public DiskCacheGpuAccessor(
|
||||||
GpuContext context,
|
GpuContext context,
|
||||||
ReadOnlyMemory<byte> data,
|
ReadOnlyMemory<byte> data,
|
||||||
@@ -36,7 +39,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
ShaderSpecializationState oldSpecState,
|
ShaderSpecializationState oldSpecState,
|
||||||
ShaderSpecializationState newSpecState,
|
ShaderSpecializationState newSpecState,
|
||||||
ResourceCounts counts,
|
ResourceCounts counts,
|
||||||
int stageIndex) : base(context, counts, stageIndex)
|
int stageIndex,
|
||||||
|
bool hasGeometryShader) : base(context, counts, stageIndex)
|
||||||
{
|
{
|
||||||
_data = data;
|
_data = data;
|
||||||
_cb1Data = cb1Data;
|
_cb1Data = cb1Data;
|
||||||
@@ -44,6 +48,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
_newSpecState = newSpecState;
|
_newSpecState = newSpecState;
|
||||||
_stageIndex = stageIndex;
|
_stageIndex = stageIndex;
|
||||||
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
|
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
|
||||||
|
_hasGeometryShader = hasGeometryShader;
|
||||||
|
_supportsQuads = context.Capabilities.SupportsQuads;
|
||||||
|
|
||||||
if (stageIndex == (int)ShaderStage.Geometry - 1)
|
if (stageIndex == (int)ShaderStage.Geometry - 1)
|
||||||
{
|
{
|
||||||
@@ -100,7 +106,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public GpuGraphicsState QueryGraphicsState()
|
public GpuGraphicsState QueryGraphicsState()
|
||||||
{
|
{
|
||||||
return _oldSpecState.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _oldSpecState.GraphicsState.YNegateEnabled);
|
return _oldSpecState.GraphicsState.CreateShaderGraphicsState(
|
||||||
|
!_isVulkan,
|
||||||
|
_supportsQuads,
|
||||||
|
_hasGeometryShader,
|
||||||
|
_isVulkan || _oldSpecState.GraphicsState.YNegateEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@@ -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 = 6577;
|
private const uint CodeGenVersion = 5936;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -601,6 +601,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
|
|
||||||
TargetApi api = _context.Capabilities.Api;
|
TargetApi api = _context.Capabilities.Api;
|
||||||
|
|
||||||
|
bool hasCachedGs = guestShaders[4].HasValue;
|
||||||
|
|
||||||
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
|
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
|
||||||
{
|
{
|
||||||
if (guestShaders[stageIndex + 1].HasValue)
|
if (guestShaders[stageIndex + 1].HasValue)
|
||||||
@@ -610,7 +612,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
byte[] guestCode = shader.Code;
|
byte[] guestCode = shader.Code;
|
||||||
byte[] cb1Data = shader.Cb1Data;
|
byte[] cb1Data = shader.Cb1Data;
|
||||||
|
|
||||||
DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex);
|
DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex, hasCachedGs);
|
||||||
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0);
|
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0);
|
||||||
|
|
||||||
if (nextStage != null)
|
if (nextStage != null)
|
||||||
@@ -623,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
byte[] guestCodeA = guestShaders[0].Value.Code;
|
byte[] guestCodeA = guestShaders[0].Value.Code;
|
||||||
byte[] cb1DataA = guestShaders[0].Value.Cb1Data;
|
byte[] cb1DataA = guestShaders[0].Value.Cb1Data;
|
||||||
|
|
||||||
DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0);
|
DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0, hasCachedGs);
|
||||||
translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0);
|
translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -711,7 +713,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
GuestCodeAndCbData shader = guestShaders[0].Value;
|
GuestCodeAndCbData shader = guestShaders[0].Value;
|
||||||
ResourceCounts counts = new();
|
ResourceCounts counts = new();
|
||||||
ShaderSpecializationState newSpecState = new(ref specState.ComputeState);
|
ShaderSpecializationState newSpecState = new(ref specState.ComputeState);
|
||||||
DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0);
|
DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0, false);
|
||||||
gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false);
|
gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false);
|
||||||
|
|
||||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0);
|
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0);
|
||||||
|
@@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
private readonly int _stageIndex;
|
private readonly int _stageIndex;
|
||||||
private readonly bool _compute;
|
private readonly bool _compute;
|
||||||
private readonly bool _isVulkan;
|
private readonly bool _isVulkan;
|
||||||
|
private readonly bool _hasGeometryShader;
|
||||||
|
private readonly bool _supportsQuads;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU state accessor for graphics shader translation.
|
/// Creates a new instance of the GPU state accessor for graphics shader translation.
|
||||||
@@ -25,12 +27,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param>
|
/// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param>
|
||||||
public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state, int stageIndex) : base(context, state.ResourceCounts, stageIndex)
|
/// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
|
||||||
|
public GpuAccessor(
|
||||||
|
GpuContext context,
|
||||||
|
GpuChannel channel,
|
||||||
|
GpuAccessorState state,
|
||||||
|
int stageIndex,
|
||||||
|
bool hasGeometryShader) : base(context, state.ResourceCounts, stageIndex)
|
||||||
{
|
{
|
||||||
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
|
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
_state = state;
|
_state = state;
|
||||||
_stageIndex = stageIndex;
|
_stageIndex = stageIndex;
|
||||||
|
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
|
||||||
|
_hasGeometryShader = hasGeometryShader;
|
||||||
|
_supportsQuads = context.Capabilities.SupportsQuads;
|
||||||
|
|
||||||
if (stageIndex == (int)ShaderStage.Geometry - 1)
|
if (stageIndex == (int)ShaderStage.Geometry - 1)
|
||||||
{
|
{
|
||||||
@@ -105,7 +115,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public GpuGraphicsState QueryGraphicsState()
|
public GpuGraphicsState QueryGraphicsState()
|
||||||
{
|
{
|
||||||
return _state.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _state.GraphicsState.YNegateEnabled);
|
return _state.GraphicsState.CreateShaderGraphicsState(
|
||||||
|
!_isVulkan,
|
||||||
|
_supportsQuads,
|
||||||
|
_hasGeometryShader,
|
||||||
|
_isVulkan || _state.GraphicsState.YNegateEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@@ -106,8 +106,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// Creates a new graphics state from this state that can be used for shader generation.
|
/// Creates a new graphics state from this state that can be used for shader generation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hostSupportsAlphaTest">Indicates if the host API supports alpha test operations</param>
|
/// <param name="hostSupportsAlphaTest">Indicates if the host API supports alpha test operations</param>
|
||||||
|
/// <param name="hostSupportsQuads">Indicates if the host API supports quad primitives</param>
|
||||||
|
/// <param name="hasGeometryShader">Indicates if a geometry shader is used</param>
|
||||||
|
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
|
||||||
/// <returns>GPU graphics state that can be used for shader translation</returns>
|
/// <returns>GPU graphics state that can be used for shader translation</returns>
|
||||||
public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool originUpperLeft)
|
public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool hostSupportsQuads, bool hasGeometryShader, bool originUpperLeft)
|
||||||
{
|
{
|
||||||
AlphaTestOp alphaTestOp;
|
AlphaTestOp alphaTestOp;
|
||||||
|
|
||||||
@@ -130,6 +133,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isQuad = Topology == PrimitiveTopology.Quads || Topology == PrimitiveTopology.QuadStrip;
|
||||||
|
bool halvePrimitiveId = !hostSupportsQuads && !hasGeometryShader && isQuad;
|
||||||
|
|
||||||
return new GpuGraphicsState(
|
return new GpuGraphicsState(
|
||||||
EarlyZForce,
|
EarlyZForce,
|
||||||
ConvertToInputTopology(Topology, TessellationMode),
|
ConvertToInputTopology(Topology, TessellationMode),
|
||||||
@@ -149,7 +155,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
in FragmentOutputTypes,
|
in FragmentOutputTypes,
|
||||||
DualSourceBlendEnable,
|
DualSourceBlendEnable,
|
||||||
YNegateEnabled,
|
YNegateEnabled,
|
||||||
originUpperLeft);
|
originUpperLeft,
|
||||||
|
halvePrimitiveId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
if (gpuVa != 0)
|
if (gpuVa != 0)
|
||||||
{
|
{
|
||||||
GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex);
|
GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex, addresses.Geometry != 0);
|
||||||
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);
|
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);
|
||||||
|
|
||||||
if (nextStage != null)
|
if (nextStage != null)
|
||||||
|
@@ -161,6 +161,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
supportsBgraFormat: false,
|
supportsBgraFormat: false,
|
||||||
supportsR4G4Format: false,
|
supportsR4G4Format: false,
|
||||||
supportsR4G4B4A4Format: true,
|
supportsR4G4B4A4Format: true,
|
||||||
|
supportsScaledVertexFormats: true,
|
||||||
supportsSnormBufferTextureFormat: false,
|
supportsSnormBufferTextureFormat: false,
|
||||||
supports5BitComponentFormat: true,
|
supports5BitComponentFormat: true,
|
||||||
supportsSparseBuffer: false,
|
supportsSparseBuffer: false,
|
||||||
@@ -175,7 +176,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
|
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
|
||||||
supportsCubemapView: true,
|
supportsCubemapView: true,
|
||||||
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
||||||
supportsScaledVertexFormats: true,
|
supportsQuads: HwCapabilities.SupportsQuads,
|
||||||
supportsSeparateSampler: false,
|
supportsSeparateSampler: false,
|
||||||
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
||||||
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
|
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
|
||||||
|
@@ -102,6 +102,11 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly bool OriginUpperLeft;
|
public readonly bool OriginUpperLeft;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool HalvePrimitiveId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU graphics state.
|
/// Creates a new GPU graphics state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -124,6 +129,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
/// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param>
|
/// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param>
|
||||||
/// <param name="yNegateEnabled">Indicates if negation of the viewport Y axis is enabled</param>
|
/// <param name="yNegateEnabled">Indicates if negation of the viewport Y axis is enabled</param>
|
||||||
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
|
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
|
||||||
|
/// <param name="halvePrimitiveId">Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion</param>
|
||||||
public GpuGraphicsState(
|
public GpuGraphicsState(
|
||||||
bool earlyZForce,
|
bool earlyZForce,
|
||||||
InputTopology topology,
|
InputTopology topology,
|
||||||
@@ -143,7 +149,8 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
in Array8<AttributeType> fragmentOutputTypes,
|
in Array8<AttributeType> fragmentOutputTypes,
|
||||||
bool dualSourceBlendEnable,
|
bool dualSourceBlendEnable,
|
||||||
bool yNegateEnabled,
|
bool yNegateEnabled,
|
||||||
bool originUpperLeft)
|
bool originUpperLeft,
|
||||||
|
bool halvePrimitiveId)
|
||||||
{
|
{
|
||||||
EarlyZForce = earlyZForce;
|
EarlyZForce = earlyZForce;
|
||||||
Topology = topology;
|
Topology = topology;
|
||||||
@@ -164,6 +171,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
DualSourceBlendEnable = dualSourceBlendEnable;
|
DualSourceBlendEnable = dualSourceBlendEnable;
|
||||||
YNegateEnabled = yNegateEnabled;
|
YNegateEnabled = yNegateEnabled;
|
||||||
OriginUpperLeft = originUpperLeft;
|
OriginUpperLeft = originUpperLeft;
|
||||||
|
HalvePrimitiveId = halvePrimitiveId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -135,6 +135,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
default,
|
default,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -84,6 +84,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
value = context.IConvertU32ToFP32(value);
|
value = context.IConvertU32ToFP32(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (offset == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
|
||||||
|
{
|
||||||
|
value = context.ShiftRightS32(value, Const(1));
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(Register(rd), value);
|
context.Copy(Register(rd), value);
|
||||||
}
|
}
|
||||||
@@ -187,6 +191,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (op.Imm10 == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
|
||||||
|
{
|
||||||
|
// If quads are used, but the host does not support them, they need to be converted to triangles.
|
||||||
|
// Since each quad becomes 2 triangles, we need to compensate here and divide primitive ID by 2.
|
||||||
|
res = context.ShiftRightS32(res, Const(1));
|
||||||
|
}
|
||||||
else if (op.Imm10 == AttributeConsts.FrontFacing && context.TranslatorContext.GpuAccessor.QueryHostHasFrontFacingBug())
|
else if (op.Imm10 == AttributeConsts.FrontFacing && context.TranslatorContext.GpuAccessor.QueryHostHasFrontFacingBug())
|
||||||
{
|
{
|
||||||
// gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.
|
// gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.
|
||||||
|
@@ -66,9 +66,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
|
|
||||||
if (nvHandle.AsgOp is not Operation handleOp ||
|
if (nvHandle.AsgOp is not Operation handleOp ||
|
||||||
handleOp.Inst != Instruction.Load ||
|
handleOp.Inst != Instruction.Load ||
|
||||||
handleOp.StorageKind != StorageKind.Input)
|
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
|
||||||
{
|
{
|
||||||
// Right now, we only allow bindless access when the handle comes from a shader input.
|
// Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
|
||||||
// This is an artificial limitation to prevent it from being used in cases where it
|
// This is an artificial limitation to prevent it from being used in cases where it
|
||||||
// would have a large performance impact of loading all textures in the pool.
|
// would have a large performance impact of loading all textures in the pool.
|
||||||
// It might be removed in the future, if we can mitigate the performance impact.
|
// It might be removed in the future, if we can mitigate the performance impact.
|
||||||
|
@@ -45,6 +45,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public bool YNegateEnabled => _graphicsState.YNegateEnabled;
|
public bool YNegateEnabled => _graphicsState.YNegateEnabled;
|
||||||
public bool OriginUpperLeft => _graphicsState.OriginUpperLeft;
|
public bool OriginUpperLeft => _graphicsState.OriginUpperLeft;
|
||||||
|
|
||||||
|
public bool HalvePrimitiveId => _graphicsState.HalvePrimitiveId;
|
||||||
|
|
||||||
public ImapPixelType[] ImapTypes { get; }
|
public ImapPixelType[] ImapTypes { get; }
|
||||||
public bool IaIndexing { get; private set; }
|
public bool IaIndexing { get; private set; }
|
||||||
public bool OaIndexing { get; private set; }
|
public bool OaIndexing { get; private set; }
|
||||||
|
@@ -691,6 +691,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportsBgraFormat: true,
|
supportsBgraFormat: true,
|
||||||
supportsR4G4Format: false,
|
supportsR4G4Format: false,
|
||||||
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
|
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
|
||||||
|
supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
|
||||||
supportsSnormBufferTextureFormat: true,
|
supportsSnormBufferTextureFormat: true,
|
||||||
supports5BitComponentFormat: supports5BitComponentFormat,
|
supports5BitComponentFormat: supports5BitComponentFormat,
|
||||||
supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit),
|
supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit),
|
||||||
@@ -705,7 +706,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportsMismatchingViewFormat: true,
|
supportsMismatchingViewFormat: true,
|
||||||
supportsCubemapView: !IsAmdGcn,
|
supportsCubemapView: !IsAmdGcn,
|
||||||
supportsNonConstantTextureOffset: false,
|
supportsNonConstantTextureOffset: false,
|
||||||
supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
|
supportsQuads: false,
|
||||||
supportsSeparateSampler: true,
|
supportsSeparateSampler: true,
|
||||||
supportsShaderBallot: false,
|
supportsShaderBallot: false,
|
||||||
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
|
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
|
||||||
|
@@ -81,6 +81,11 @@ namespace Ryujinx.Input.GTK3
|
|||||||
return _pressedKeys.Contains(nativeKey);
|
return _pressedKeys.Contains(nativeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_pressedKeys.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
public IGamepad GetGamepad(string id)
|
public IGamepad GetGamepad(string id)
|
||||||
{
|
{
|
||||||
if (!_keyboardIdentifers[0].Equals(id))
|
if (!_keyboardIdentifers[0].Equals(id))
|
||||||
|
@@ -75,7 +75,7 @@ namespace Ryujinx
|
|||||||
|
|
||||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
||||||
{
|
{
|
||||||
MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning);
|
MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
|
@@ -63,7 +63,7 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'">
|
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
|
||||||
<Content Include="..\..\distribution\linux\Ryujinx.sh">
|
<Content Include="..\..\distribution\linux\Ryujinx.sh">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@@ -107,6 +107,8 @@ namespace Ryujinx.UI.Applet
|
|||||||
swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
||||||
swkbdDialog.SetInputValidation(args.KeyboardMode);
|
swkbdDialog.SetInputValidation(args.KeyboardMode);
|
||||||
|
|
||||||
|
((MainWindow)_parent).RendererWidget.NpadManager.BlockInputUpdates();
|
||||||
|
|
||||||
if (swkbdDialog.Run() == (int)ResponseType.Ok)
|
if (swkbdDialog.Run() == (int)ResponseType.Ok)
|
||||||
{
|
{
|
||||||
inputText = swkbdDialog.InputEntry.Text;
|
inputText = swkbdDialog.InputEntry.Text;
|
||||||
@@ -128,6 +130,7 @@ namespace Ryujinx.UI.Applet
|
|||||||
});
|
});
|
||||||
|
|
||||||
dialogCloseEvent.WaitOne();
|
dialogCloseEvent.WaitOne();
|
||||||
|
((MainWindow)_parent).RendererWidget.NpadManager.UnblockInputUpdates();
|
||||||
|
|
||||||
userText = error ? null : inputText;
|
userText = error ? null : inputText;
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
private bool _sixAxisSensorFusionEnabled;
|
private bool _sixAxisSensorFusionEnabled;
|
||||||
private bool _unintendedHomeButtonInputProtectionEnabled;
|
private bool _unintendedHomeButtonInputProtectionEnabled;
|
||||||
|
private bool _npadAnalogStickCenterClampEnabled;
|
||||||
private bool _vibrationPermitted;
|
private bool _vibrationPermitted;
|
||||||
private bool _usbFullKeyControllerEnabled;
|
private bool _usbFullKeyControllerEnabled;
|
||||||
private readonly bool _isFirmwareUpdateAvailableForSixAxisSensor;
|
private readonly bool _isFirmwareUpdateAvailableForSixAxisSensor;
|
||||||
@@ -1107,6 +1108,19 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
// If not, it returns nothing.
|
// If not, it returns nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(134)] // 6.1.0+
|
||||||
|
// SetNpadUseAnalogStickUseCenterClamp(bool Enable, nn::applet::AppletResourceUserId)
|
||||||
|
public ResultCode SetNpadUseAnalogStickUseCenterClamp(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong pid = context.RequestData.ReadUInt64();
|
||||||
|
_npadAnalogStickCenterClampEnabled = context.RequestData.ReadUInt32() != 0;
|
||||||
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { pid, appletResourceUserId, _npadAnalogStickCenterClampEnabled });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(200)]
|
[CommandCmif(200)]
|
||||||
// GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo
|
// GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo
|
||||||
public ResultCode GetVibrationDeviceInfo(ServiceCtx context)
|
public ResultCode GetVibrationDeviceInfo(ServiceCtx context)
|
||||||
|
@@ -48,7 +48,7 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'">
|
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
|
||||||
<Content Include="..\..\distribution\linux\Ryujinx.sh">
|
<Content Include="..\..\distribution\linux\Ryujinx.sh">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@@ -174,6 +174,11 @@ namespace Ryujinx.Input.HLE
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
foreach (InputConfig inputConfig in _inputConfig)
|
||||||
|
{
|
||||||
|
_controllers[(int)inputConfig.PlayerIndex].GamepadDriver.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
_blockInputUpdates = false;
|
_blockInputUpdates = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,5 +33,11 @@ namespace Ryujinx.Input
|
|||||||
/// <param name="id">The unique id of the gamepad</param>
|
/// <param name="id">The unique id of the gamepad</param>
|
||||||
/// <returns>An instance of <see cref="IGamepad"/> associated to the gamepad id given or null if not found</returns>
|
/// <returns>An instance of <see cref="IGamepad"/> associated to the gamepad id given or null if not found</returns>
|
||||||
IGamepad GetGamepad(string id);
|
IGamepad GetGamepad(string id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flush the internal state of the driver.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Does nothing by default.</remarks>
|
||||||
|
void Clear() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using DiscordRPC;
|
using DiscordRPC;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.UI.Common.Configuration;
|
using Ryujinx.UI.Common.Configuration;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.UI.Common
|
namespace Ryujinx.UI.Common
|
||||||
{
|
{
|
||||||
@@ -9,6 +10,9 @@ namespace Ryujinx.UI.Common
|
|||||||
private const string Description = "A simple, experimental Nintendo Switch emulator.";
|
private const string Description = "A simple, experimental Nintendo Switch emulator.";
|
||||||
private const string ApplicationId = "1216775165866807456";
|
private const string ApplicationId = "1216775165866807456";
|
||||||
|
|
||||||
|
private const int ApplicationByteLimit = 128;
|
||||||
|
private const string Ellipsis = "…";
|
||||||
|
|
||||||
private static DiscordRpcClient _discordClient;
|
private static DiscordRpcClient _discordClient;
|
||||||
private static RichPresence _discordPresenceMain;
|
private static RichPresence _discordPresenceMain;
|
||||||
|
|
||||||
@@ -60,18 +64,18 @@ namespace Ryujinx.UI.Common
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SwitchToPlayingState(string titleId, string titleName)
|
public static void SwitchToPlayingState(string titleId, string applicationName)
|
||||||
{
|
{
|
||||||
_discordClient?.SetPresence(new RichPresence
|
_discordClient?.SetPresence(new RichPresence
|
||||||
{
|
{
|
||||||
Assets = new Assets
|
Assets = new Assets
|
||||||
{
|
{
|
||||||
LargeImageKey = "game",
|
LargeImageKey = "game",
|
||||||
LargeImageText = titleName,
|
LargeImageText = TruncateToByteLength(applicationName, ApplicationByteLimit),
|
||||||
SmallImageKey = "ryujinx",
|
SmallImageKey = "ryujinx",
|
||||||
SmallImageText = Description,
|
SmallImageText = Description,
|
||||||
},
|
},
|
||||||
Details = $"Playing {titleName}",
|
Details = TruncateToByteLength($"Playing {applicationName}", ApplicationByteLimit),
|
||||||
State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(),
|
State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(),
|
||||||
Timestamps = Timestamps.Now,
|
Timestamps = Timestamps.Now,
|
||||||
Buttons =
|
Buttons =
|
||||||
@@ -90,6 +94,28 @@ namespace Ryujinx.UI.Common
|
|||||||
_discordClient?.SetPresence(_discordPresenceMain);
|
_discordClient?.SetPresence(_discordPresenceMain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string TruncateToByteLength(string input, int byteLimit)
|
||||||
|
{
|
||||||
|
if (Encoding.UTF8.GetByteCount(input) <= byteLimit)
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the length to trim the string to guarantee we have space for the trailing ellipsis.
|
||||||
|
int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis);
|
||||||
|
|
||||||
|
// Basic trim to best case scenario of 1 byte characters.
|
||||||
|
input = input[..trimLimit];
|
||||||
|
|
||||||
|
while (Encoding.UTF8.GetByteCount(input) > trimLimit)
|
||||||
|
{
|
||||||
|
// Remove one character from the end of the string at a time.
|
||||||
|
input = input[..^1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return input.TrimEnd() + Ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Exit()
|
public static void Exit()
|
||||||
{
|
{
|
||||||
_discordClient?.Dispose();
|
_discordClient?.Dispose();
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Platform;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using Ryujinx.Ava.Common;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
@@ -84,7 +86,7 @@ namespace Ryujinx.Ava
|
|||||||
ApplyConfiguredTheme();
|
ApplyConfiguredTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyConfiguredTheme()
|
public void ApplyConfiguredTheme()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -92,13 +94,18 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(baseStyle))
|
if (string.IsNullOrWhiteSpace(baseStyle))
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.UI.BaseStyle.Value = "Dark";
|
ConfigurationState.Instance.UI.BaseStyle.Value = "Auto";
|
||||||
|
|
||||||
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThemeVariant systemTheme = DetectSystemTheme();
|
||||||
|
|
||||||
|
ThemeManager.OnThemeChanged();
|
||||||
|
|
||||||
RequestedThemeVariant = baseStyle switch
|
RequestedThemeVariant = baseStyle switch
|
||||||
{
|
{
|
||||||
|
"Auto" => systemTheme,
|
||||||
"Light" => ThemeVariant.Light,
|
"Light" => ThemeVariant.Light,
|
||||||
"Dark" => ThemeVariant.Dark,
|
"Dark" => ThemeVariant.Dark,
|
||||||
_ => ThemeVariant.Default,
|
_ => ThemeVariant.Default,
|
||||||
@@ -111,5 +118,28 @@ namespace Ryujinx.Ava
|
|||||||
ShowRestartDialog();
|
ShowRestartDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a PlatformThemeVariant value to the corresponding ThemeVariant value.
|
||||||
|
/// </summary>
|
||||||
|
public static ThemeVariant ConvertThemeVariant(PlatformThemeVariant platformThemeVariant) =>
|
||||||
|
platformThemeVariant switch
|
||||||
|
{
|
||||||
|
PlatformThemeVariant.Dark => ThemeVariant.Dark,
|
||||||
|
PlatformThemeVariant.Light => ThemeVariant.Light,
|
||||||
|
_ => ThemeVariant.Default,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static ThemeVariant DetectSystemTheme()
|
||||||
|
{
|
||||||
|
if (Application.Current is App app)
|
||||||
|
{
|
||||||
|
var colorValues = app.PlatformSettings.GetColorValues();
|
||||||
|
|
||||||
|
return ConvertThemeVariant(colorValues.ThemeVariant);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ThemeVariant.Default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -404,6 +404,7 @@
|
|||||||
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
||||||
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
||||||
"SettingsTabGeneralTheme": "Theme:",
|
"SettingsTabGeneralTheme": "Theme:",
|
||||||
|
"SettingsTabGeneralThemeAuto": "Auto",
|
||||||
"SettingsTabGeneralThemeDark": "Dark",
|
"SettingsTabGeneralThemeDark": "Dark",
|
||||||
"SettingsTabGeneralThemeLight": "Light",
|
"SettingsTabGeneralThemeLight": "Light",
|
||||||
"ControllerSettingsConfigureGeneral": "Configure",
|
"ControllerSettingsConfigureGeneral": "Configure",
|
||||||
|
14
src/Ryujinx/Common/ThemeManager.cs
Normal file
14
src/Ryujinx/Common/ThemeManager.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Common
|
||||||
|
{
|
||||||
|
public static class ThemeManager
|
||||||
|
{
|
||||||
|
public static event EventHandler ThemeChanged;
|
||||||
|
|
||||||
|
public static void OnThemeChanged()
|
||||||
|
{
|
||||||
|
ThemeChanged?.Invoke(null, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -195,7 +195,7 @@ namespace Ryujinx.Ava.Input
|
|||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
_driver?.ResetKeys();
|
_driver?.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
@@ -94,7 +94,7 @@ namespace Ryujinx.Ava.Input
|
|||||||
return _pressedKeys.Contains(nativeKey);
|
return _pressedKeys.Contains(nativeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetKeys()
|
public void Clear()
|
||||||
{
|
{
|
||||||
_pressedKeys.Clear();
|
_pressedKeys.Clear();
|
||||||
}
|
}
|
||||||
|
@@ -40,7 +40,7 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
||||||
{
|
{
|
||||||
_ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
_ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviewerDetached = true;
|
PreviewerDetached = true;
|
||||||
|
@@ -70,7 +70,8 @@
|
|||||||
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
<ProjectReference Include="..\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -88,7 +89,7 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'">
|
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
|
||||||
<Content Include="..\..\distribution\linux\Ryujinx.sh">
|
<Content Include="..\..\distribution\linux\Ryujinx.sh">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@@ -122,6 +122,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_parent.ViewModel.AppHost.NpadManager.BlockInputUpdates();
|
||||||
var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
|
var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
|
||||||
|
|
||||||
if (response.Result == UserResult.Ok)
|
if (response.Result == UserResult.Ok)
|
||||||
@@ -143,6 +144,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
});
|
});
|
||||||
|
|
||||||
dialogCloseEvent.WaitOne();
|
dialogCloseEvent.WaitOne();
|
||||||
|
_parent.ViewModel.AppHost.NpadManager.UnblockInputUpdates();
|
||||||
|
|
||||||
userText = error ? null : inputText;
|
userText = error ? null : inputText;
|
||||||
|
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using Ryujinx.Ava.Common;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.UI.Common.Configuration;
|
using Ryujinx.UI.Common.Configuration;
|
||||||
@@ -11,7 +13,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
public class AboutWindowViewModel : BaseModel
|
public class AboutWindowViewModel : BaseModel, IDisposable
|
||||||
{
|
{
|
||||||
private Bitmap _githubLogo;
|
private Bitmap _githubLogo;
|
||||||
private Bitmap _discordLogo;
|
private Bitmap _discordLogo;
|
||||||
@@ -86,23 +88,39 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public AboutWindowViewModel()
|
public AboutWindowViewModel()
|
||||||
{
|
{
|
||||||
Version = Program.Version;
|
Version = Program.Version;
|
||||||
|
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value);
|
||||||
if (ConfigurationState.Instance.UI.BaseStyle.Value == "Light")
|
|
||||||
{
|
|
||||||
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.UI.Common")));
|
|
||||||
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.UI.Common")));
|
|
||||||
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.UI.Common")));
|
|
||||||
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.UI.Common")));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.UI.Common")));
|
|
||||||
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.UI.Common")));
|
|
||||||
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.UI.Common")));
|
|
||||||
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.UI.Common")));
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);
|
Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);
|
||||||
|
|
||||||
|
ThemeManager.ThemeChanged += ThemeManager_ThemeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThemeManager_ThemeChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLogoTheme(string theme)
|
||||||
|
{
|
||||||
|
bool isDarkTheme = theme == "Dark" || (theme == "Auto" && App.DetectSystemTheme() == ThemeVariant.Dark);
|
||||||
|
|
||||||
|
string basePath = "resm:Ryujinx.UI.Common.Resources.";
|
||||||
|
string themeSuffix = isDarkTheme ? "Dark.png" : "Light.png";
|
||||||
|
|
||||||
|
GithubLogo = LoadBitmap($"{basePath}Logo_GitHub_{themeSuffix}?assembly=Ryujinx.UI.Common");
|
||||||
|
DiscordLogo = LoadBitmap($"{basePath}Logo_Discord_{themeSuffix}?assembly=Ryujinx.UI.Common");
|
||||||
|
PatreonLogo = LoadBitmap($"{basePath}Logo_Patreon_{themeSuffix}?assembly=Ryujinx.UI.Common");
|
||||||
|
TwitterLogo = LoadBitmap($"{basePath}Logo_Twitter_{themeSuffix}?assembly=Ryujinx.UI.Common");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap LoadBitmap(string uri)
|
||||||
|
{
|
||||||
|
return new Bitmap(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ThemeManager.ThemeChanged -= ThemeManager_ThemeChanged;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadPatronsJson()
|
private async Task DownloadPatronsJson()
|
||||||
|
@@ -397,7 +397,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
GameDirectories.Clear();
|
GameDirectories.Clear();
|
||||||
GameDirectories.AddRange(config.UI.GameDirs.Value);
|
GameDirectories.AddRange(config.UI.GameDirs.Value);
|
||||||
|
|
||||||
BaseStyleIndex = config.UI.BaseStyle == "Light" ? 0 : 1;
|
BaseStyleIndex = config.UI.BaseStyle.Value switch
|
||||||
|
{
|
||||||
|
"Auto" => 0,
|
||||||
|
"Light" => 1,
|
||||||
|
"Dark" => 2,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
EnableDockedMode = config.System.EnableDockedMode;
|
EnableDockedMode = config.System.EnableDockedMode;
|
||||||
@@ -412,10 +418,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Language = (int)config.System.Language.Value;
|
Language = (int)config.System.Language.Value;
|
||||||
TimeZone = config.System.TimeZone;
|
TimeZone = config.System.TimeZone;
|
||||||
|
|
||||||
DateTime currentDateTime = DateTime.Now;
|
DateTime currentHostDateTime = DateTime.Now;
|
||||||
|
TimeSpan systemDateTimeOffset = TimeSpan.FromSeconds(config.System.SystemTimeOffset);
|
||||||
|
DateTime currentDateTime = currentHostDateTime.Add(systemDateTimeOffset);
|
||||||
CurrentDate = currentDateTime.Date;
|
CurrentDate = currentDateTime.Date;
|
||||||
CurrentTime = currentDateTime.TimeOfDay.Add(TimeSpan.FromSeconds(config.System.SystemTimeOffset));
|
CurrentTime = currentDateTime.TimeOfDay;
|
||||||
|
|
||||||
EnableVsync = config.Graphics.EnableVsync;
|
EnableVsync = config.Graphics.EnableVsync;
|
||||||
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
||||||
@@ -485,7 +492,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.UI.GameDirs.Value = gameDirs;
|
config.UI.GameDirs.Value = gameDirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.UI.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
|
config.UI.BaseStyle.Value = BaseStyleIndex switch
|
||||||
|
{
|
||||||
|
0 => "Auto",
|
||||||
|
1 => "Light",
|
||||||
|
2 => "Dark",
|
||||||
|
_ => "Auto"
|
||||||
|
};
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
config.System.EnableDockedMode.Value = EnableDockedMode;
|
config.System.EnableDockedMode.Value = EnableDockedMode;
|
||||||
|
@@ -65,6 +65,9 @@
|
|||||||
<ComboBox SelectedIndex="{Binding BaseStyleIndex}"
|
<ComboBox SelectedIndex="{Binding BaseStyleIndex}"
|
||||||
HorizontalContentAlignment="Left"
|
HorizontalContentAlignment="Left"
|
||||||
MinWidth="100">
|
MinWidth="100">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeAuto}" />
|
||||||
|
</ComboBoxItem>
|
||||||
<ComboBoxItem>
|
<ComboBoxItem>
|
||||||
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeLight}" />
|
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeLight}" />
|
||||||
</ComboBoxItem>
|
</ComboBoxItem>
|
||||||
|
@@ -2,6 +2,7 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Platform;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using Ryujinx.Ava.Common;
|
using Ryujinx.Ava.Common;
|
||||||
@@ -92,6 +93,29 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event handler for detecting OS theme change when using "Follow OS theme" option
|
||||||
|
/// </summary>
|
||||||
|
private void OnPlatformColorValuesChanged(object sender, PlatformColorValues e)
|
||||||
|
{
|
||||||
|
if (Application.Current is App app)
|
||||||
|
{
|
||||||
|
app.ApplyConfiguredTheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnClosed(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnClosed(e);
|
||||||
|
if (PlatformSettings != null)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribe to the ColorValuesChanged event
|
||||||
|
/// </summary>
|
||||||
|
PlatformSettings.ColorValuesChanged -= OnPlatformColorValuesChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
@@ -390,6 +414,11 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribe to the ColorValuesChanged event
|
||||||
|
/// </summary>
|
||||||
|
PlatformSettings.ColorValuesChanged += OnPlatformColorValuesChanged;
|
||||||
|
|
||||||
ViewModel.Initialize(
|
ViewModel.Initialize(
|
||||||
ContentManager,
|
ContentManager,
|
||||||
StorageProvider,
|
StorageProvider,
|
||||||
|
Reference in New Issue
Block a user