Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
ac4f2c1e70 | |||
e40470bbe1 | |||
f460ecc182 | |||
086564c3c8 | |||
b6ac45d36d | |||
7afae8c699 | |||
7835968214 | |||
0aceb534cb | |||
a0af6e4d07 | |||
f61b7818c3 | |||
a2a97e1b11 | |||
8b2625b0be | |||
651e24fed9 |
@ -20,7 +20,7 @@
|
|||||||
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
|
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
|
||||||
<PackageVersion Include="LibHac" Version="0.18.0" />
|
<PackageVersion Include="LibHac" Version="0.18.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace ARMeilleure.Diagnostics
|
namespace ARMeilleure.Diagnostics
|
||||||
{
|
{
|
||||||
@ -33,7 +34,6 @@ namespace ARMeilleure.Diagnostics
|
|||||||
|
|
||||||
public static string Get(ulong address)
|
public static string Get(ulong address)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (_symbols.TryGetValue(address, out string result))
|
if (_symbols.TryGetValue(address, out string result))
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
@ -48,13 +48,15 @@ namespace ARMeilleure.Diagnostics
|
|||||||
ulong diff = address - symbol.Start;
|
ulong diff = address - symbol.Start;
|
||||||
ulong rem = diff % symbol.ElementSize;
|
ulong rem = diff % symbol.ElementSize;
|
||||||
|
|
||||||
result = symbol.Name + "_" + diff / symbol.ElementSize;
|
StringBuilder resultBuilder = new();
|
||||||
|
resultBuilder.Append($"{symbol.Name}_{diff / symbol.ElementSize}");
|
||||||
|
|
||||||
if (rem != 0)
|
if (rem != 0)
|
||||||
{
|
{
|
||||||
result += "+" + rem;
|
resultBuilder.Append($"+{rem}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = resultBuilder.ToString();
|
||||||
_symbols.TryAdd(address, result);
|
_symbols.TryAdd(address, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -327,7 +327,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
string imageUrl = _amiiboList.Find(amiibo => amiibo.Equals(selected)).Image;
|
string imageUrl = _amiiboList.Find(amiibo => amiibo.Equals(selected)).Image;
|
||||||
|
|
||||||
string usageString = "";
|
StringBuilder usageStringBuilder = new();
|
||||||
|
|
||||||
for (int i = 0; i < _amiiboList.Count; i++)
|
for (int i = 0; i < _amiiboList.Count; i++)
|
||||||
{
|
{
|
||||||
@ -341,20 +341,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
|
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
|
||||||
{
|
{
|
||||||
usageString += Environment.NewLine +
|
usageStringBuilder.Append($"{Environment.NewLine}- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}");
|
||||||
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
|
||||||
|
|
||||||
writable = usageItem.Write;
|
writable = usageItem.Write;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usageString.Length == 0)
|
if (usageStringBuilder.Length == 0)
|
||||||
{
|
{
|
||||||
usageString = LocaleManager.Instance[LocaleKeys.Unknown] + ".";
|
usageStringBuilder.Append($"{LocaleManager.Instance[LocaleKeys.Unknown]}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageString}";
|
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageStringBuilder}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1278,6 +1278,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Glyph = Glyph.Grid;
|
Glyph = Glyph.Grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAspectRatio(AspectRatio aspectRatio)
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.Graphics.AspectRatio.Value = aspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task InstallFirmwareFromFile()
|
public async Task InstallFirmwareFromFile()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
|
@ -147,6 +147,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public bool EnableTextureRecompression { get; set; }
|
public bool EnableTextureRecompression { get; set; }
|
||||||
public bool EnableMacroHLE { get; set; }
|
public bool EnableMacroHLE { get; set; }
|
||||||
public bool EnableColorSpacePassthrough { get; set; }
|
public bool EnableColorSpacePassthrough { get; set; }
|
||||||
|
public bool ColorSpacePassthroughAvailable => IsMacOS;
|
||||||
public bool EnableFileLog { get; set; }
|
public bool EnableFileLog { get; set; }
|
||||||
public bool EnableStub { get; set; }
|
public bool EnableStub { get; set; }
|
||||||
public bool EnableInfo { get; set; }
|
public bool EnableInfo { get; set; }
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
|
xmlns:config="clr-namespace:Ryujinx.Common.Configuration;assembly=Ryujinx.Common"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"
|
x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"
|
||||||
x:DataType="viewModels:MainWindowViewModel">
|
x:DataType="viewModels:MainWindowViewModel">
|
||||||
@ -112,15 +113,52 @@
|
|||||||
Background="Gray"
|
Background="Gray"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
IsVisible="{Binding !ShowLoadProgress}" />
|
IsVisible="{Binding !ShowLoadProgress}" />
|
||||||
<TextBlock
|
<SplitButton
|
||||||
Name="AspectRatioStatus"
|
Name="AspectRatioStatus"
|
||||||
Margin="5,0,5,0"
|
Padding="5,0,5,0"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
CornerRadius="0"
|
||||||
IsVisible="{Binding !ShowLoadProgress}"
|
IsVisible="{Binding !ShowLoadProgress}"
|
||||||
PointerReleased="AspectRatioStatus_PointerReleased"
|
Content="{Binding AspectRatioStatusText}"
|
||||||
Text="{Binding AspectRatioStatusText}"
|
Click="AspectRatioStatus_OnClick"
|
||||||
TextAlignment="Left" />
|
ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
|
||||||
|
<SplitButton.Styles>
|
||||||
|
<Style Selector="Border#SeparatorBorder">
|
||||||
|
<Setter Property="Opacity" Value="0" />
|
||||||
|
</Style>
|
||||||
|
</SplitButton.Styles>
|
||||||
|
<SplitButton.Flyout>
|
||||||
|
<MenuFlyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||||
|
<MenuItem
|
||||||
|
Header="{locale:Locale SettingsTabGraphicsAspectRatio4x3}"
|
||||||
|
Command="{Binding SetAspectRatio}"
|
||||||
|
CommandParameter="{x:Static config:AspectRatio.Fixed4x3}"/>
|
||||||
|
<MenuItem
|
||||||
|
Header="{locale:Locale SettingsTabGraphicsAspectRatio16x9}"
|
||||||
|
Command="{Binding SetAspectRatio}"
|
||||||
|
CommandParameter="{x:Static config:AspectRatio.Fixed16x9}"/>
|
||||||
|
<MenuItem
|
||||||
|
Header="{locale:Locale SettingsTabGraphicsAspectRatio16x10}"
|
||||||
|
Command="{Binding SetAspectRatio}"
|
||||||
|
CommandParameter="{x:Static config:AspectRatio.Fixed16x10}"/>
|
||||||
|
<MenuItem
|
||||||
|
Header="{locale:Locale SettingsTabGraphicsAspectRatio21x9}"
|
||||||
|
Command="{Binding SetAspectRatio}"
|
||||||
|
CommandParameter="{x:Static config:AspectRatio.Fixed21x9}"/>
|
||||||
|
<MenuItem
|
||||||
|
Header="{locale:Locale SettingsTabGraphicsAspectRatio32x9}"
|
||||||
|
Command="{Binding SetAspectRatio}"
|
||||||
|
CommandParameter="{x:Static config:AspectRatio.Fixed32x9}"/>
|
||||||
|
<MenuItem
|
||||||
|
Header="{locale:Locale SettingsTabGraphicsAspectRatioStretch}"
|
||||||
|
Command="{Binding SetAspectRatio}"
|
||||||
|
CommandParameter="{x:Static config:AspectRatio.Stretched}"/>
|
||||||
|
</MenuFlyout>
|
||||||
|
</SplitButton.Flyout>
|
||||||
|
</SplitButton>
|
||||||
<Border
|
<Border
|
||||||
Width="2"
|
Width="2"
|
||||||
Height="12"
|
Height="12"
|
||||||
|
@ -43,10 +43,9 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
|
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
private void AspectRatioStatus_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
|
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
|
||||||
|
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
|
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
|
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
<CheckBox IsChecked="{Binding EnableColorSpacePassthrough}"
|
<CheckBox IsChecked="{Binding EnableColorSpacePassthrough}"
|
||||||
|
IsVisible="{Binding ColorSpacePassthroughAvailable}"
|
||||||
ToolTip.Tip="{locale:Locale SettingsEnableColorSpacePassthroughTooltip}">
|
ToolTip.Tip="{locale:Locale SettingsEnableColorSpacePassthroughTooltip}">
|
||||||
<TextBlock Text="{locale:Locale SettingsEnableColorSpacePassthrough}" />
|
<TextBlock Text="{locale:Locale SettingsEnableColorSpacePassthrough}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
@ -756,6 +756,18 @@ namespace Ryujinx.Common.Memory
|
|||||||
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
|
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct Array96<T> : IArray<T> where T : unmanaged
|
||||||
|
{
|
||||||
|
T _e0;
|
||||||
|
Array64<T> _other;
|
||||||
|
Array31<T> _other2;
|
||||||
|
public readonly int Length => 96;
|
||||||
|
public ref T this[int index] => ref AsSpan()[index];
|
||||||
|
|
||||||
|
[Pure]
|
||||||
|
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
|
||||||
|
}
|
||||||
|
|
||||||
public struct Array127<T> : IArray<T> where T : unmanaged
|
public struct Array127<T> : IArray<T> where T : unmanaged
|
||||||
{
|
{
|
||||||
T _e0;
|
T _e0;
|
||||||
|
@ -32,6 +32,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ref TState State => ref _state.State;
|
public ref TState State => ref _state.State;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current shadow state.
|
||||||
|
/// </summary>
|
||||||
|
public ref TState ShadowState => ref _shadowState.State;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the device state, with shadow state.
|
/// Creates a new instance of the device state, with shadow state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@ -15,9 +18,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
private const int ColorLayerCountOffset = 0x818;
|
private const int ColorLayerCountOffset = 0x818;
|
||||||
private const int ColorStructSize = 0x40;
|
private const int ColorStructSize = 0x40;
|
||||||
private const int ZetaLayerCountOffset = 0x1230;
|
private const int ZetaLayerCountOffset = 0x1230;
|
||||||
|
private const int UniformBufferBindVertexOffset = 0x2410;
|
||||||
|
private const int FirstVertexOffset = 0x1434;
|
||||||
|
|
||||||
private const int IndirectIndexedDataEntrySize = 0x14;
|
private const int IndirectIndexedDataEntrySize = 0x14;
|
||||||
|
|
||||||
|
private const int LogicOpOffset = 0x19c4;
|
||||||
|
private const int ShaderIdScratchOffset = 0x3470;
|
||||||
|
private const int ShaderAddressScratchOffset = 0x3488;
|
||||||
|
private const int UpdateConstantBufferAddressesBase = 0x34a8;
|
||||||
|
private const int UpdateConstantBufferSizesBase = 0x34bc;
|
||||||
|
private const int UpdateConstantBufferAddressCbu = 0x3460;
|
||||||
|
|
||||||
private readonly GPFifoProcessor _processor;
|
private readonly GPFifoProcessor _processor;
|
||||||
private readonly MacroHLEFunctionName _functionName;
|
private readonly MacroHLEFunctionName _functionName;
|
||||||
|
|
||||||
@ -49,6 +61,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
{
|
{
|
||||||
switch (_functionName)
|
switch (_functionName)
|
||||||
{
|
{
|
||||||
|
case MacroHLEFunctionName.BindShaderProgram:
|
||||||
|
BindShaderProgram(state, arg0);
|
||||||
|
break;
|
||||||
case MacroHLEFunctionName.ClearColor:
|
case MacroHLEFunctionName.ClearColor:
|
||||||
ClearColor(state, arg0);
|
ClearColor(state, arg0);
|
||||||
break;
|
break;
|
||||||
@ -58,6 +73,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
case MacroHLEFunctionName.DrawArraysInstanced:
|
case MacroHLEFunctionName.DrawArraysInstanced:
|
||||||
DrawArraysInstanced(state, arg0);
|
DrawArraysInstanced(state, arg0);
|
||||||
break;
|
break;
|
||||||
|
case MacroHLEFunctionName.DrawElements:
|
||||||
|
DrawElements(state, arg0);
|
||||||
|
break;
|
||||||
case MacroHLEFunctionName.DrawElementsInstanced:
|
case MacroHLEFunctionName.DrawElementsInstanced:
|
||||||
DrawElementsInstanced(state, arg0);
|
DrawElementsInstanced(state, arg0);
|
||||||
break;
|
break;
|
||||||
@ -67,6 +85,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
|
case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
|
||||||
MultiDrawElementsIndirectCount(state, arg0);
|
MultiDrawElementsIndirectCount(state, arg0);
|
||||||
break;
|
break;
|
||||||
|
case MacroHLEFunctionName.UpdateBlendState:
|
||||||
|
UpdateBlendState(state, arg0);
|
||||||
|
break;
|
||||||
|
case MacroHLEFunctionName.UpdateColorMasks:
|
||||||
|
UpdateColorMasks(state, arg0);
|
||||||
|
break;
|
||||||
|
case MacroHLEFunctionName.UpdateUniformBufferState:
|
||||||
|
UpdateUniformBufferState(state, arg0);
|
||||||
|
break;
|
||||||
|
case MacroHLEFunctionName.UpdateUniformBufferStateCbu:
|
||||||
|
UpdateUniformBufferStateCbu(state, arg0);
|
||||||
|
break;
|
||||||
|
case MacroHLEFunctionName.UpdateUniformBufferStateCbuV2:
|
||||||
|
UpdateUniformBufferStateCbuV2(state, arg0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException(_functionName.ToString());
|
throw new NotImplementedException(_functionName.ToString());
|
||||||
}
|
}
|
||||||
@ -75,6 +108,149 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
Fifo.Clear();
|
Fifo.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a shader program with the index in arg0.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state at the time of the call</param>
|
||||||
|
/// <param name="arg0">First argument of the call</param>
|
||||||
|
private void BindShaderProgram(IDeviceState state, int arg0)
|
||||||
|
{
|
||||||
|
int scratchOffset = ShaderIdScratchOffset + arg0 * 4;
|
||||||
|
|
||||||
|
int lastId = state.Read(scratchOffset);
|
||||||
|
int id = FetchParam().Word;
|
||||||
|
int offset = FetchParam().Word;
|
||||||
|
|
||||||
|
if (lastId == id)
|
||||||
|
{
|
||||||
|
FetchParam();
|
||||||
|
FetchParam();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_processor.ThreedClass.SetShaderOffset(arg0, (uint)offset);
|
||||||
|
|
||||||
|
// Removes overflow on the method address into the increment portion.
|
||||||
|
// Present in the original macro.
|
||||||
|
int addrMask = unchecked((int)0xfffc0fff) << 2;
|
||||||
|
|
||||||
|
state.Write(scratchOffset & addrMask, id);
|
||||||
|
state.Write((ShaderAddressScratchOffset + arg0 * 4) & addrMask, offset);
|
||||||
|
|
||||||
|
int stage = FetchParam().Word;
|
||||||
|
uint cbAddress = (uint)FetchParam().Word;
|
||||||
|
|
||||||
|
_processor.ThreedClass.UpdateUniformBufferState(65536, cbAddress >> 24, cbAddress << 8);
|
||||||
|
|
||||||
|
int stageOffset = (stage & 0x7f) << 3;
|
||||||
|
|
||||||
|
state.Write((UniformBufferBindVertexOffset + stageOffset * 4) & addrMask, 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates uniform buffer state for update or bind.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state at the time of the call</param>
|
||||||
|
/// <param name="arg0">First argument of the call</param>
|
||||||
|
private void UpdateUniformBufferState(IDeviceState state, int arg0)
|
||||||
|
{
|
||||||
|
uint address = (uint)state.Read(UpdateConstantBufferAddressesBase + arg0 * 4);
|
||||||
|
int size = state.Read(UpdateConstantBufferSizesBase + arg0 * 4);
|
||||||
|
|
||||||
|
_processor.ThreedClass.UpdateUniformBufferState(size, address >> 24, address << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates uniform buffer state for update.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state at the time of the call</param>
|
||||||
|
/// <param name="arg0">First argument of the call</param>
|
||||||
|
private void UpdateUniformBufferStateCbu(IDeviceState state, int arg0)
|
||||||
|
{
|
||||||
|
uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
|
||||||
|
|
||||||
|
UniformBufferState ubState = new()
|
||||||
|
{
|
||||||
|
Address = new()
|
||||||
|
{
|
||||||
|
High = address >> 24,
|
||||||
|
Low = address << 8
|
||||||
|
},
|
||||||
|
Size = 24320,
|
||||||
|
Offset = arg0 << 2
|
||||||
|
};
|
||||||
|
|
||||||
|
_processor.ThreedClass.UpdateUniformBufferState(ubState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates uniform buffer state for update.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state at the time of the call</param>
|
||||||
|
/// <param name="arg0">First argument of the call</param>
|
||||||
|
private void UpdateUniformBufferStateCbuV2(IDeviceState state, int arg0)
|
||||||
|
{
|
||||||
|
uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
|
||||||
|
|
||||||
|
UniformBufferState ubState = new()
|
||||||
|
{
|
||||||
|
Address = new()
|
||||||
|
{
|
||||||
|
High = address >> 24,
|
||||||
|
Low = address << 8
|
||||||
|
},
|
||||||
|
Size = 28672,
|
||||||
|
Offset = arg0 << 2
|
||||||
|
};
|
||||||
|
|
||||||
|
_processor.ThreedClass.UpdateUniformBufferState(ubState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates blend enable using the given argument.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state at the time of the call</param>
|
||||||
|
/// <param name="arg0">First argument of the call</param>
|
||||||
|
private void UpdateBlendState(IDeviceState state, int arg0)
|
||||||
|
{
|
||||||
|
state.Write(LogicOpOffset, 0);
|
||||||
|
|
||||||
|
Array8<Boolean32> enable = new();
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
enable[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_processor.ThreedClass.UpdateBlendEnable(ref enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates color masks using the given argument and three pushed arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state at the time of the call</param>
|
||||||
|
/// <param name="arg0">First argument of the call</param>
|
||||||
|
private void UpdateColorMasks(IDeviceState state, int arg0)
|
||||||
|
{
|
||||||
|
Array8<RtColorMask> masks = new();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
masks[index++] = new RtColorMask((uint)arg0 & 0x1fff);
|
||||||
|
masks[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff);
|
||||||
|
|
||||||
|
if (i != 3)
|
||||||
|
{
|
||||||
|
arg0 = FetchParam().Word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_processor.ThreedClass.UpdateColorMasks(ref masks);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clears one bound color target.
|
/// Clears one bound color target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -129,6 +305,36 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
indexed: false);
|
indexed: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a indexed draw.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state at the time of the call</param>
|
||||||
|
/// <param name="arg0">First argument of the call</param>
|
||||||
|
private void DrawElements(IDeviceState state, int arg0)
|
||||||
|
{
|
||||||
|
var topology = (PrimitiveTopology)arg0;
|
||||||
|
|
||||||
|
var indexAddressHigh = FetchParam();
|
||||||
|
var indexAddressLow = FetchParam();
|
||||||
|
var indexType = FetchParam();
|
||||||
|
var firstIndex = 0;
|
||||||
|
var indexCount = FetchParam();
|
||||||
|
|
||||||
|
_processor.ThreedClass.UpdateIndexBuffer(
|
||||||
|
(uint)indexAddressHigh.Word,
|
||||||
|
(uint)indexAddressLow.Word,
|
||||||
|
(IndexType)indexType.Word);
|
||||||
|
|
||||||
|
_processor.ThreedClass.Draw(
|
||||||
|
topology,
|
||||||
|
indexCount.Word,
|
||||||
|
1,
|
||||||
|
firstIndex,
|
||||||
|
state.Read(FirstVertexOffset),
|
||||||
|
0,
|
||||||
|
indexed: true);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a indexed draw.
|
/// Performs a indexed draw.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -6,11 +6,19 @@
|
|||||||
enum MacroHLEFunctionName
|
enum MacroHLEFunctionName
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
BindShaderProgram,
|
||||||
ClearColor,
|
ClearColor,
|
||||||
ClearDepthStencil,
|
ClearDepthStencil,
|
||||||
DrawArraysInstanced,
|
DrawArraysInstanced,
|
||||||
|
DrawElements,
|
||||||
DrawElementsInstanced,
|
DrawElementsInstanced,
|
||||||
DrawElementsIndirect,
|
DrawElementsIndirect,
|
||||||
MultiDrawElementsIndirectCount,
|
MultiDrawElementsIndirectCount,
|
||||||
|
|
||||||
|
UpdateBlendState,
|
||||||
|
UpdateColorMasks,
|
||||||
|
UpdateUniformBufferState,
|
||||||
|
UpdateUniformBufferStateCbu,
|
||||||
|
UpdateUniformBufferStateCbuV2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
|
|
||||||
private static readonly TableEntry[] _table = new TableEntry[]
|
private static readonly TableEntry[] _table = new TableEntry[]
|
||||||
{
|
{
|
||||||
|
new(MacroHLEFunctionName.BindShaderProgram, new Hash128(0x5d5efb912369f60b, 0x69131ed5019f08ef), 0x68),
|
||||||
new(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
|
new(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
|
||||||
new(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
|
new(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
|
||||||
new(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48),
|
new(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48),
|
||||||
|
new(MacroHLEFunctionName.DrawElements, new Hash128(0x3D7F32AE6C2702A7, 0x9353C9F41C1A244D), 0x20),
|
||||||
new(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c),
|
new(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c),
|
||||||
new(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c),
|
new(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c),
|
||||||
new(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C),
|
new(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C),
|
||||||
|
new(MacroHLEFunctionName.UpdateBlendState, new Hash128(0x40F6D4E7B08D7640, 0x82167BEEAECB959F), 0x28),
|
||||||
|
new(MacroHLEFunctionName.UpdateColorMasks, new Hash128(0x9EE32420B8441DFD, 0x6E7724759A57333E), 0x24),
|
||||||
|
new(MacroHLEFunctionName.UpdateUniformBufferState, new Hash128(0x8EE66706049CB0B0, 0x51C1CF906EC86F7C), 0x20),
|
||||||
|
new(MacroHLEFunctionName.UpdateUniformBufferStateCbu, new Hash128(0xA4592676A3E581A0, 0xA39E77FE19FE04AC), 0x18),
|
||||||
|
new(MacroHLEFunctionName.UpdateUniformBufferStateCbuV2, new Hash128(0x392FA750489983D4, 0x35BACE455155D2C3), 0x18)
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -62,18 +69,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
/// <returns>True if the host supports the HLE macro, false otherwise</returns>
|
/// <returns>True if the host supports the HLE macro, false otherwise</returns>
|
||||||
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
|
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
|
||||||
{
|
{
|
||||||
if (name == MacroHLEFunctionName.ClearColor ||
|
if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
|
||||||
name == MacroHLEFunctionName.ClearDepthStencil ||
|
|
||||||
name == MacroHLEFunctionName.DrawArraysInstanced ||
|
|
||||||
name == MacroHLEFunctionName.DrawElementsInstanced ||
|
|
||||||
name == MacroHLEFunctionName.DrawElementsIndirect)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
|
|
||||||
{
|
{
|
||||||
return caps.SupportsIndirectParameters;
|
return caps.SupportsIndirectParameters;
|
||||||
}
|
}
|
||||||
|
else if (name != MacroHLEFunctionName.None)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,22 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
MethodPassthrough = 2,
|
MethodPassthrough = 2,
|
||||||
MethodReplay = 3,
|
MethodReplay = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class SetMmeShadowRamControlModeExtensions
|
||||||
|
{
|
||||||
|
public static bool IsTrack(this SetMmeShadowRamControlMode mode)
|
||||||
|
{
|
||||||
|
return mode == SetMmeShadowRamControlMode.MethodTrack || mode == SetMmeShadowRamControlMode.MethodTrackWithFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsPassthrough(this SetMmeShadowRamControlMode mode)
|
||||||
|
{
|
||||||
|
return mode == SetMmeShadowRamControlMode.MethodPassthrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsReplay(this SetMmeShadowRamControlMode mode)
|
||||||
|
{
|
||||||
|
return mode == SetMmeShadowRamControlMode.MethodReplay;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
class StateUpdater
|
class StateUpdater
|
||||||
{
|
{
|
||||||
public const int ShaderStateIndex = 26;
|
public const int ShaderStateIndex = 26;
|
||||||
|
public const int RtColorMaskIndex = 14;
|
||||||
public const int RasterizerStateIndex = 15;
|
public const int RasterizerStateIndex = 15;
|
||||||
public const int ScissorStateIndex = 16;
|
public const int ScissorStateIndex = 16;
|
||||||
public const int VertexBufferStateIndex = 0;
|
public const int VertexBufferStateIndex = 0;
|
||||||
|
public const int BlendStateIndex = 2;
|
||||||
public const int IndexBufferStateIndex = 23;
|
public const int IndexBufferStateIndex = 23;
|
||||||
public const int PrimitiveRestartStateIndex = 12;
|
public const int PrimitiveRestartStateIndex = 12;
|
||||||
public const int RenderTargetStateIndex = 27;
|
public const int RenderTargetStateIndex = 27;
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
{
|
{
|
||||||
@ -26,6 +29,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
private readonly ConstantBufferUpdater _cbUpdater;
|
private readonly ConstantBufferUpdater _cbUpdater;
|
||||||
private readonly StateUpdater _stateUpdater;
|
private readonly StateUpdater _stateUpdater;
|
||||||
|
|
||||||
|
private SetMmeShadowRamControlMode ShadowMode => _state.State.SetMmeShadowRamControlMode;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the 3D engine class.
|
/// Creates a new instance of the 3D engine class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -228,6 +233,206 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
_cbUpdater.Update(data);
|
_cbUpdater.Update(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test if two 32 byte structs are equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of the 32-byte struct</typeparam>
|
||||||
|
/// <param name="lhs">First struct</param>
|
||||||
|
/// <param name="rhs">Second struct</param>
|
||||||
|
/// <returns>True if equal, false otherwise</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool UnsafeEquals32Byte<T>(ref T lhs, ref T rhs) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (Vector256.IsHardwareAccelerated)
|
||||||
|
{
|
||||||
|
return Vector256.EqualsAll(
|
||||||
|
Unsafe.As<T, Vector256<uint>>(ref lhs),
|
||||||
|
Unsafe.As<T, Vector256<uint>>(ref rhs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ref var lhsVec = ref Unsafe.As<T, Vector128<uint>>(ref lhs);
|
||||||
|
ref var rhsVec = ref Unsafe.As<T, Vector128<uint>>(ref rhs);
|
||||||
|
|
||||||
|
return Vector128.EqualsAll(lhsVec, rhsVec) &&
|
||||||
|
Vector128.EqualsAll(Unsafe.Add(ref lhsVec, 1), Unsafe.Add(ref rhsVec, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates blend enable. Respects current shadow mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="masks">Blend enable</param>
|
||||||
|
public void UpdateBlendEnable(ref Array8<Boolean32> enable)
|
||||||
|
{
|
||||||
|
var shadow = ShadowMode;
|
||||||
|
ref var state = ref _state.State.BlendEnable;
|
||||||
|
|
||||||
|
if (shadow.IsReplay())
|
||||||
|
{
|
||||||
|
enable = _state.ShadowState.BlendEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UnsafeEquals32Byte(ref enable, ref state))
|
||||||
|
{
|
||||||
|
state = enable;
|
||||||
|
|
||||||
|
_stateUpdater.ForceDirty(StateUpdater.BlendStateIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shadow.IsTrack())
|
||||||
|
{
|
||||||
|
_state.ShadowState.BlendEnable = enable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates color masks. Respects current shadow mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="masks">Color masks</param>
|
||||||
|
public void UpdateColorMasks(ref Array8<RtColorMask> masks)
|
||||||
|
{
|
||||||
|
var shadow = ShadowMode;
|
||||||
|
ref var state = ref _state.State.RtColorMask;
|
||||||
|
|
||||||
|
if (shadow.IsReplay())
|
||||||
|
{
|
||||||
|
masks = _state.ShadowState.RtColorMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UnsafeEquals32Byte(ref masks, ref state))
|
||||||
|
{
|
||||||
|
state = masks;
|
||||||
|
|
||||||
|
_stateUpdater.ForceDirty(StateUpdater.RtColorMaskIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shadow.IsTrack())
|
||||||
|
{
|
||||||
|
_state.ShadowState.RtColorMask = masks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates index buffer state for an indexed draw. Respects current shadow mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="addrHigh">High part of the address</param>
|
||||||
|
/// <param name="addrLow">Low part of the address</param>
|
||||||
|
/// <param name="type">Type of the binding</param>
|
||||||
|
public void UpdateIndexBuffer(uint addrHigh, uint addrLow, IndexType type)
|
||||||
|
{
|
||||||
|
var shadow = ShadowMode;
|
||||||
|
ref var state = ref _state.State.IndexBufferState;
|
||||||
|
|
||||||
|
if (shadow.IsReplay())
|
||||||
|
{
|
||||||
|
ref var shadowState = ref _state.ShadowState.IndexBufferState;
|
||||||
|
addrHigh = shadowState.Address.High;
|
||||||
|
addrLow = shadowState.Address.Low;
|
||||||
|
type = shadowState.Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.Address.High != addrHigh || state.Address.Low != addrLow || state.Type != type)
|
||||||
|
{
|
||||||
|
state.Address.High = addrHigh;
|
||||||
|
state.Address.Low = addrLow;
|
||||||
|
state.Type = type;
|
||||||
|
|
||||||
|
_stateUpdater.ForceDirty(StateUpdater.IndexBufferStateIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shadow.IsTrack())
|
||||||
|
{
|
||||||
|
ref var shadowState = ref _state.ShadowState.IndexBufferState;
|
||||||
|
shadowState.Address.High = addrHigh;
|
||||||
|
shadowState.Address.Low = addrLow;
|
||||||
|
shadowState.Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates uniform buffer state for update or bind. Respects current shadow mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">Size of the binding</param>
|
||||||
|
/// <param name="addrHigh">High part of the addrsss</param>
|
||||||
|
/// <param name="addrLow">Low part of the address</param>
|
||||||
|
public void UpdateUniformBufferState(int size, uint addrHigh, uint addrLow)
|
||||||
|
{
|
||||||
|
var shadow = ShadowMode;
|
||||||
|
ref var state = ref _state.State.UniformBufferState;
|
||||||
|
|
||||||
|
if (shadow.IsReplay())
|
||||||
|
{
|
||||||
|
ref var shadowState = ref _state.ShadowState.UniformBufferState;
|
||||||
|
size = shadowState.Size;
|
||||||
|
addrHigh = shadowState.Address.High;
|
||||||
|
addrLow = shadowState.Address.Low;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Size = size;
|
||||||
|
state.Address.High = addrHigh;
|
||||||
|
state.Address.Low = addrLow;
|
||||||
|
|
||||||
|
if (shadow.IsTrack())
|
||||||
|
{
|
||||||
|
ref var shadowState = ref _state.ShadowState.UniformBufferState;
|
||||||
|
shadowState.Size = size;
|
||||||
|
shadowState.Address.High = addrHigh;
|
||||||
|
shadowState.Address.Low = addrLow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a shader offset. Respects current shadow mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index of the shader to update</param>
|
||||||
|
/// <param name="offset">Offset to update with</param>
|
||||||
|
public void SetShaderOffset(int index, uint offset)
|
||||||
|
{
|
||||||
|
var shadow = ShadowMode;
|
||||||
|
ref var shaderState = ref _state.State.ShaderState[index];
|
||||||
|
|
||||||
|
if (shadow.IsReplay())
|
||||||
|
{
|
||||||
|
offset = _state.ShadowState.ShaderState[index].Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shaderState.Offset != offset)
|
||||||
|
{
|
||||||
|
shaderState.Offset = offset;
|
||||||
|
|
||||||
|
_stateUpdater.ForceDirty(StateUpdater.ShaderStateIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shadow.IsTrack())
|
||||||
|
{
|
||||||
|
_state.ShadowState.ShaderState[index].Offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates uniform buffer state for update. Respects current shadow mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ubState">Uniform buffer state</param>
|
||||||
|
public void UpdateUniformBufferState(UniformBufferState ubState)
|
||||||
|
{
|
||||||
|
var shadow = ShadowMode;
|
||||||
|
ref var state = ref _state.State.UniformBufferState;
|
||||||
|
|
||||||
|
if (shadow.IsReplay())
|
||||||
|
{
|
||||||
|
ubState = _state.ShadowState.UniformBufferState;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = ubState;
|
||||||
|
|
||||||
|
if (shadow.IsTrack())
|
||||||
|
{
|
||||||
|
_state.ShadowState.UniformBufferState = ubState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Launches the Inline-to-Memory DMA copy operation.
|
/// Launches the Inline-to-Memory DMA copy operation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -590,9 +590,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
struct RtColorMask
|
struct RtColorMask
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649 // Field is never assigned to
|
|
||||||
public uint Packed;
|
public uint Packed;
|
||||||
#pragma warning restore CS0649
|
|
||||||
|
public RtColorMask(uint packed)
|
||||||
|
{
|
||||||
|
Packed = packed;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unpacks red channel enable.
|
/// Unpacks red channel enable.
|
||||||
|
@ -5,9 +5,12 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
readonly struct Boolean32
|
readonly struct Boolean32
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649 // Field is never assigned to
|
|
||||||
private readonly uint _value;
|
private readonly uint _value;
|
||||||
#pragma warning restore CS0649
|
|
||||||
|
public Boolean32(uint value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
public static implicit operator bool(Boolean32 value)
|
public static implicit operator bool(Boolean32 value)
|
||||||
{
|
{
|
||||||
|
@ -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 = 5682;
|
private const uint CodeGenVersion = 5767;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@ -92,14 +92,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
private static string GetIndentation(int level)
|
private static string GetIndentation(int level)
|
||||||
{
|
{
|
||||||
string indentation = string.Empty;
|
StringBuilder indentationBuilder = new();
|
||||||
|
|
||||||
for (int index = 0; index < level; index++)
|
for (int index = 0; index < level; index++)
|
||||||
{
|
{
|
||||||
indentation += Tab;
|
indentationBuilder.Append(Tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
return indentation;
|
return indentationBuilder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenBallot;
|
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenBallot;
|
||||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenCall;
|
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenCall;
|
||||||
@ -67,11 +68,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
|
|
||||||
int arity = (int)(info.Type & InstType.ArityMask);
|
int arity = (int)(info.Type & InstType.ArityMask);
|
||||||
|
|
||||||
string args = string.Empty;
|
StringBuilder builder = new();
|
||||||
|
|
||||||
if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory))
|
if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory))
|
||||||
{
|
{
|
||||||
args = GenerateLoadOrStore(context, operation, isStore: false);
|
builder.Append(GenerateLoadOrStore(context, operation, isStore: false));
|
||||||
|
|
||||||
AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32
|
AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32
|
||||||
? AggregateType.S32
|
? AggregateType.S32
|
||||||
@ -79,7 +80,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
|
|
||||||
for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++)
|
for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++)
|
||||||
{
|
{
|
||||||
args += ", " + GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
builder.Append($", {GetSoureExpr(context, operation.GetSource(argIndex), dstType)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -88,16 +89,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
{
|
{
|
||||||
if (argIndex != 0)
|
if (argIndex != 0)
|
||||||
{
|
{
|
||||||
args += ", ";
|
builder.Append(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
||||||
|
|
||||||
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
builder.Append(GetSoureExpr(context, operation.GetSource(argIndex), dstType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return info.OpName + '(' + args + ')';
|
return $"{info.OpName}({builder})";
|
||||||
}
|
}
|
||||||
else if ((info.Type & InstType.Op) != 0)
|
else if ((info.Type & InstType.Op) != 0)
|
||||||
{
|
{
|
||||||
@ -184,8 +185,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
case Instruction.TextureSample:
|
case Instruction.TextureSample:
|
||||||
return TextureSample(context, operation);
|
return TextureSample(context, operation);
|
||||||
|
|
||||||
case Instruction.TextureSize:
|
case Instruction.TextureQuerySamples:
|
||||||
return TextureSize(context, operation);
|
return TextureQuerySamples(context, operation);
|
||||||
|
|
||||||
|
case Instruction.TextureQuerySize:
|
||||||
|
return TextureQuerySize(context, operation);
|
||||||
|
|
||||||
case Instruction.UnpackDouble2x32:
|
case Instruction.UnpackDouble2x32:
|
||||||
return UnpackDouble2x32(context, operation);
|
return UnpackDouble2x32(context, operation);
|
||||||
|
@ -118,7 +118,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.Subtract, InstType.OpBinary, "-", 2);
|
Add(Instruction.Subtract, InstType.OpBinary, "-", 2);
|
||||||
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
||||||
Add(Instruction.TextureSample, InstType.Special);
|
Add(Instruction.TextureSample, InstType.Special);
|
||||||
Add(Instruction.TextureSize, InstType.Special);
|
Add(Instruction.TextureQuerySamples, InstType.Special);
|
||||||
|
Add(Instruction.TextureQuerySize, InstType.Special);
|
||||||
Add(Instruction.Truncate, InstType.CallUnary, "trunc");
|
Add(Instruction.Truncate, InstType.CallUnary, "trunc");
|
||||||
Add(Instruction.UnpackDouble2x32, InstType.Special);
|
Add(Instruction.UnpackDouble2x32, InstType.Special);
|
||||||
Add(Instruction.UnpackHalf2x16, InstType.Special);
|
Add(Instruction.UnpackHalf2x16, InstType.Special);
|
||||||
|
@ -517,7 +517,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
return texCall;
|
return texCall;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string TextureSize(CodeGenContext context, AstOperation operation)
|
public static string TextureQuerySamples(CodeGenContext context, AstOperation operation)
|
||||||
|
{
|
||||||
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
|
||||||
|
// TODO: Bindless texture support. For now we just return 0.
|
||||||
|
if (isBindless)
|
||||||
|
{
|
||||||
|
return NumberFormatter.FormatInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
string indexExpr = null;
|
||||||
|
|
||||||
|
if (isIndexed)
|
||||||
|
{
|
||||||
|
indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
|
||||||
|
}
|
||||||
|
|
||||||
|
string samplerName = GetSamplerName(context.Properties, texOp, indexExpr);
|
||||||
|
|
||||||
|
return $"textureSamples({samplerName})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string TextureQuerySize(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
|
||||||
@ -753,17 +779,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
|
|
||||||
private static string GetMaskMultiDest(int mask)
|
private static string GetMaskMultiDest(int mask)
|
||||||
{
|
{
|
||||||
string swizzle = ".";
|
StringBuilder swizzleBuilder = new();
|
||||||
|
swizzleBuilder.Append('.');
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
if ((mask & (1 << i)) != 0)
|
if ((mask & (1 << i)) != 0)
|
||||||
{
|
{
|
||||||
swizzle += "xyzw"[i];
|
swizzleBuilder.Append("xyzw"[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return swizzle;
|
return swizzleBuilder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
public StructuredFunction CurrentFunction { get; set; }
|
public StructuredFunction CurrentFunction { get; set; }
|
||||||
private readonly Dictionary<AstOperand, Instruction> _locals = new();
|
private readonly Dictionary<AstOperand, Instruction> _locals = new();
|
||||||
private readonly Dictionary<int, Instruction[]> _localForArgs = new();
|
|
||||||
private readonly Dictionary<int, Instruction> _funcArgs = new();
|
private readonly Dictionary<int, Instruction> _funcArgs = new();
|
||||||
private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new();
|
private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new();
|
||||||
|
|
||||||
@ -112,7 +111,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
IsMainFunction = isMainFunction;
|
IsMainFunction = isMainFunction;
|
||||||
MayHaveReturned = false;
|
MayHaveReturned = false;
|
||||||
_locals.Clear();
|
_locals.Clear();
|
||||||
_localForArgs.Clear();
|
|
||||||
_funcArgs.Clear();
|
_funcArgs.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,11 +167,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
_locals.Add(local, spvLocal);
|
_locals.Add(local, spvLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeclareLocalForArgs(int funcIndex, Instruction[] spvLocals)
|
|
||||||
{
|
|
||||||
_localForArgs.Add(funcIndex, spvLocals);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeclareArgument(int argIndex, Instruction spvLocal)
|
public void DeclareArgument(int argIndex, Instruction spvLocal)
|
||||||
{
|
{
|
||||||
_funcArgs.Add(argIndex, spvLocal);
|
_funcArgs.Add(argIndex, spvLocal);
|
||||||
@ -278,11 +271,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return _locals[local];
|
return _locals[local];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instruction[] GetLocalForArgsPointers(int funcIndex)
|
|
||||||
{
|
|
||||||
return _localForArgs[funcIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction GetArgumentPointer(AstOperand funcArg)
|
public Instruction GetArgumentPointer(AstOperand funcArg)
|
||||||
{
|
{
|
||||||
return _funcArgs[funcArg.Value];
|
return _funcArgs[funcArg.Value];
|
||||||
|
@ -41,28 +41,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
|
|
||||||
{
|
|
||||||
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
|
||||||
{
|
|
||||||
StructuredFunction function = functions[funcIndex];
|
|
||||||
SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < function.InArguments.Length; i++)
|
|
||||||
{
|
|
||||||
var type = function.GetArgumentType(i);
|
|
||||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
|
|
||||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
|
||||||
|
|
||||||
context.AddLocalVariable(spvLocal);
|
|
||||||
|
|
||||||
locals[i] = spvLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.DeclareLocalForArgs(funcIndex, locals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
|
public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
|
||||||
{
|
{
|
||||||
DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values);
|
DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values);
|
||||||
|
@ -134,7 +134,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Add(Instruction.Subtract, GenerateSubtract);
|
Add(Instruction.Subtract, GenerateSubtract);
|
||||||
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
||||||
Add(Instruction.TextureSample, GenerateTextureSample);
|
Add(Instruction.TextureSample, GenerateTextureSample);
|
||||||
Add(Instruction.TextureSize, GenerateTextureSize);
|
Add(Instruction.TextureQuerySamples, GenerateTextureQuerySamples);
|
||||||
|
Add(Instruction.TextureQuerySize, GenerateTextureQuerySize);
|
||||||
Add(Instruction.Truncate, GenerateTruncate);
|
Add(Instruction.Truncate, GenerateTruncate);
|
||||||
Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32);
|
Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32);
|
||||||
Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16);
|
Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16);
|
||||||
@ -310,26 +311,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var (function, spvFunc) = context.GetFunction(funcId.Value);
|
var (function, spvFunc) = context.GetFunction(funcId.Value);
|
||||||
|
|
||||||
var args = new SpvInstruction[operation.SourcesCount - 1];
|
var args = new SpvInstruction[operation.SourcesCount - 1];
|
||||||
var spvLocals = context.GetLocalForArgsPointers(funcId.Value);
|
|
||||||
|
|
||||||
for (int i = 0; i < args.Length; i++)
|
for (int i = 0; i < args.Length; i++)
|
||||||
{
|
{
|
||||||
var operand = operation.GetSource(i + 1);
|
var operand = operation.GetSource(i + 1);
|
||||||
|
|
||||||
if (i >= function.InArguments.Length)
|
AstOperand local = (AstOperand)operand;
|
||||||
{
|
Debug.Assert(local.Type == OperandType.LocalVariable);
|
||||||
args[i] = context.GetLocalPointer((AstOperand)operand);
|
args[i] = context.GetLocalPointer(local);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var type = function.GetArgumentType(i);
|
|
||||||
var value = context.Get(type, operand);
|
|
||||||
var spvLocal = spvLocals[i];
|
|
||||||
|
|
||||||
context.Store(spvLocal, value);
|
|
||||||
|
|
||||||
args[i] = spvLocal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var retType = function.ReturnType;
|
var retType = function.ReturnType;
|
||||||
@ -1492,7 +1481,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return new OperationResult(swizzledResultType, result);
|
return new OperationResult(swizzledResultType, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateTextureSize(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateTextureQuerySamples(CodeGenContext context, AstOperation operation)
|
||||||
|
{
|
||||||
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
|
||||||
|
// TODO: Bindless texture support. For now we just return 0.
|
||||||
|
if (isBindless)
|
||||||
|
{
|
||||||
|
return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
if (isIndexed)
|
||||||
|
{
|
||||||
|
context.GetS32(texOp.GetSource(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
(var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
|
||||||
|
|
||||||
|
var image = context.Load(sampledImageType, sampledImageVariable);
|
||||||
|
image = context.Image(imageType, image);
|
||||||
|
|
||||||
|
SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image);
|
||||||
|
|
||||||
|
return new OperationResult(AggregateType.S32, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OperationResult GenerateTextureQuerySize(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
|
||||||
|
@ -161,7 +161,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.EnterBlock(function.MainBlock);
|
context.EnterBlock(function.MainBlock);
|
||||||
|
|
||||||
Declarations.DeclareLocals(context, function);
|
Declarations.DeclareLocals(context, function);
|
||||||
Declarations.DeclareLocalForArgs(context, info.Functions);
|
|
||||||
|
|
||||||
Generate(context, function.MainBlock);
|
Generate(context, function.MainBlock);
|
||||||
|
|
||||||
|
@ -1093,18 +1093,29 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
SamplerType type;
|
SamplerType type;
|
||||||
|
|
||||||
if (isBindless)
|
if (isBindless)
|
||||||
|
{
|
||||||
|
if (query == TexQuery.TexHeaderTextureType)
|
||||||
|
{
|
||||||
|
type = SamplerType.Texture2D | SamplerType.Multisample;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
|
type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
type = context.TranslatorContext.GpuAccessor.QuerySamplerType(imm);
|
type = context.TranslatorContext.GpuAccessor.QuerySamplerType(imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
|
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
|
||||||
|
int binding;
|
||||||
|
|
||||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
switch (query)
|
||||||
Instruction.TextureSize,
|
{
|
||||||
|
case TexQuery.TexHeaderDimension:
|
||||||
|
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||||
|
Instruction.TextureQuerySize,
|
||||||
type,
|
type,
|
||||||
TextureFormat.Unknown,
|
TextureFormat.Unknown,
|
||||||
flags,
|
flags,
|
||||||
@ -1122,12 +1133,46 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Validate and use query parameter.
|
context.Copy(d, context.TextureQuerySize(type, flags, binding, compIndex, sources));
|
||||||
Operand res = context.TextureSize(type, flags, binding, compIndex, sources);
|
|
||||||
|
|
||||||
context.Copy(d, res);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TexQuery.TexHeaderTextureType:
|
||||||
|
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||||
|
Instruction.TextureQuerySamples,
|
||||||
|
type,
|
||||||
|
TextureFormat.Unknown,
|
||||||
|
flags,
|
||||||
|
TextureOperation.DefaultCbufSlot,
|
||||||
|
imm);
|
||||||
|
|
||||||
|
if ((componentMask & 4) != 0)
|
||||||
|
{
|
||||||
|
// Skip first 2 components if necessary.
|
||||||
|
if ((componentMask & 1) != 0)
|
||||||
|
{
|
||||||
|
GetDest();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((componentMask & 2) != 0)
|
||||||
|
{
|
||||||
|
GetDest();
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand d = GetDest();
|
||||||
|
|
||||||
|
if (d != null)
|
||||||
|
{
|
||||||
|
context.Copy(d, context.TextureQuerySamples(type, flags, binding, sources));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
context.TranslatorContext.GpuAccessor.Log($"Invalid or unsupported query type \"{query}\".");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitTextureSample(
|
private static void EmitTextureSample(
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
[SuppressMessage("Design", "CA1069: Enums values should not be duplicated")]
|
|
||||||
enum Instruction
|
enum Instruction
|
||||||
{
|
{
|
||||||
Absolute = 1,
|
Absolute = 1,
|
||||||
@ -118,7 +116,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
Subtract,
|
Subtract,
|
||||||
SwizzleAdd,
|
SwizzleAdd,
|
||||||
TextureSample,
|
TextureSample,
|
||||||
TextureSize,
|
TextureQuerySamples,
|
||||||
|
TextureQuerySize,
|
||||||
Truncate,
|
Truncate,
|
||||||
UnpackDouble2x32,
|
UnpackDouble2x32,
|
||||||
UnpackHalf2x16,
|
UnpackHalf2x16,
|
||||||
@ -160,7 +159,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
public static bool IsTextureQuery(this Instruction inst)
|
public static bool IsTextureQuery(this Instruction inst)
|
||||||
{
|
{
|
||||||
inst &= Instruction.Mask;
|
inst &= Instruction.Mask;
|
||||||
return inst == Instruction.Lod || inst == Instruction.TextureSize;
|
return inst == Instruction.Lod || inst == Instruction.TextureQuerySamples || inst == Instruction.TextureQuerySize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
|
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
|
||||||
Add(Instruction.TextureSample, AggregateType.FP32);
|
Add(Instruction.TextureSample, AggregateType.FP32);
|
||||||
Add(Instruction.TextureSize, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.TextureQuerySamples, AggregateType.S32, AggregateType.S32);
|
||||||
|
Add(Instruction.TextureQuerySize, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.Truncate, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Truncate, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.UnpackDouble2x32, AggregateType.U32, AggregateType.FP64);
|
Add(Instruction.UnpackDouble2x32, AggregateType.U32, AggregateType.FP64);
|
||||||
Add(Instruction.UnpackHalf2x16, AggregateType.FP32, AggregateType.U32);
|
Add(Instruction.UnpackHalf2x16, AggregateType.FP32, AggregateType.U32);
|
||||||
|
@ -2,17 +2,22 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
{
|
{
|
||||||
static class StructuredProgram
|
static class StructuredProgram
|
||||||
{
|
{
|
||||||
|
// TODO: Eventually it should be possible to specify the parameter types for the function instead of using S32 for everything.
|
||||||
|
private const AggregateType FuncParameterType = AggregateType.S32;
|
||||||
|
|
||||||
public static StructuredProgramInfo MakeStructuredProgram(
|
public static StructuredProgramInfo MakeStructuredProgram(
|
||||||
IReadOnlyList<Function> functions,
|
IReadOnlyList<Function> functions,
|
||||||
AttributeUsage attributeUsage,
|
AttributeUsage attributeUsage,
|
||||||
ShaderDefinitions definitions,
|
ShaderDefinitions definitions,
|
||||||
ResourceManager resourceManager,
|
ResourceManager resourceManager,
|
||||||
|
TargetLanguage targetLanguage,
|
||||||
bool debugMode)
|
bool debugMode)
|
||||||
{
|
{
|
||||||
StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode);
|
StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode);
|
||||||
@ -23,19 +28,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
BasicBlock[] blocks = function.Blocks;
|
BasicBlock[] blocks = function.Blocks;
|
||||||
|
|
||||||
AggregateType returnType = function.ReturnsValue ? AggregateType.S32 : AggregateType.Void;
|
AggregateType returnType = function.ReturnsValue ? FuncParameterType : AggregateType.Void;
|
||||||
|
|
||||||
AggregateType[] inArguments = new AggregateType[function.InArgumentsCount];
|
AggregateType[] inArguments = new AggregateType[function.InArgumentsCount];
|
||||||
AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount];
|
AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount];
|
||||||
|
|
||||||
for (int i = 0; i < inArguments.Length; i++)
|
for (int i = 0; i < inArguments.Length; i++)
|
||||||
{
|
{
|
||||||
inArguments[i] = AggregateType.S32;
|
inArguments[i] = FuncParameterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < outArguments.Length; i++)
|
for (int i = 0; i < outArguments.Length; i++)
|
||||||
{
|
{
|
||||||
outArguments[i] = AggregateType.S32;
|
outArguments[i] = FuncParameterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.EnterFunction(blocks.Length, function.Name, returnType, inArguments, outArguments);
|
context.EnterFunction(blocks.Length, function.Name, returnType, inArguments, outArguments);
|
||||||
@ -58,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddOperation(context, operation);
|
AddOperation(context, operation, targetLanguage, functions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
return context.Info;
|
return context.Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddOperation(StructuredProgramContext context, Operation operation)
|
private static void AddOperation(StructuredProgramContext context, Operation operation, TargetLanguage targetLanguage, IReadOnlyList<Function> functions)
|
||||||
{
|
{
|
||||||
Instruction inst = operation.Inst;
|
Instruction inst = operation.Inst;
|
||||||
StorageKind storageKind = operation.StorageKind;
|
StorageKind storageKind = operation.StorageKind;
|
||||||
@ -114,10 +119,44 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
|
IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
|
||||||
|
|
||||||
|
if (inst == Instruction.Call && targetLanguage == TargetLanguage.Spirv)
|
||||||
|
{
|
||||||
|
// SPIR-V requires that all function parameters are copied to a local variable before the call
|
||||||
|
// (or at least that's what the Khronos compiler does).
|
||||||
|
|
||||||
|
// First one is the function index.
|
||||||
|
Operand funcIndexOperand = operation.GetSource(0);
|
||||||
|
Debug.Assert(funcIndexOperand.Type == OperandType.Constant);
|
||||||
|
int funcIndex = funcIndexOperand.Value;
|
||||||
|
|
||||||
|
sources[0] = new AstOperand(OperandType.Constant, funcIndex);
|
||||||
|
|
||||||
|
int inArgsCount = functions[funcIndex].InArgumentsCount;
|
||||||
|
|
||||||
|
// Remaining ones are parameters, copy them to a temp local variable.
|
||||||
|
for (int index = 1; index < operation.SourcesCount; index++)
|
||||||
|
{
|
||||||
|
IAstNode source = context.GetOperandOrCbLoad(operation.GetSource(index));
|
||||||
|
|
||||||
|
if (index - 1 < inArgsCount)
|
||||||
|
{
|
||||||
|
AstOperand argTemp = context.NewTemp(FuncParameterType);
|
||||||
|
context.AddNode(new AstAssignment(argTemp, source));
|
||||||
|
sources[index] = argTemp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sources[index] = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
for (int index = 0; index < operation.SourcesCount; index++)
|
||||||
{
|
{
|
||||||
sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index));
|
sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < outDestsCount; index++)
|
for (int index = 0; index < outDestsCount; index++)
|
||||||
{
|
{
|
||||||
|
@ -897,7 +897,21 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources));
|
context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand TextureSize(
|
public static Operand TextureQuerySamples(
|
||||||
|
this EmitterContext context,
|
||||||
|
SamplerType type,
|
||||||
|
TextureFlags flags,
|
||||||
|
int binding,
|
||||||
|
Operand[] sources)
|
||||||
|
{
|
||||||
|
Operand dest = Local();
|
||||||
|
|
||||||
|
context.Add(new TextureOperation(Instruction.TextureQuerySamples, type, TextureFormat.Unknown, flags, binding, 0, new[] { dest }, sources));
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Operand TextureQuerySize(
|
||||||
this EmitterContext context,
|
this EmitterContext context,
|
||||||
SamplerType type,
|
SamplerType type,
|
||||||
TextureFlags flags,
|
TextureFlags flags,
|
||||||
@ -907,7 +921,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
Operand dest = Local();
|
Operand dest = Local();
|
||||||
|
|
||||||
context.Add(new TextureOperation(Instruction.TextureSize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
|
context.Add(new TextureOperation(Instruction.TextureQuerySize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texOp.Inst == Instruction.Lod ||
|
if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
|
||||||
texOp.Inst == Instruction.TextureSample ||
|
|
||||||
texOp.Inst == Instruction.TextureSize)
|
|
||||||
{
|
{
|
||||||
Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block);
|
Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block);
|
||||||
|
|
||||||
@ -40,7 +38,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
// as long bindless elimination is successful and we know where the texture descriptor is located.
|
// as long bindless elimination is successful and we know where the texture descriptor is located.
|
||||||
bool rewriteSamplerType =
|
bool rewriteSamplerType =
|
||||||
texOp.Type == SamplerType.TextureBuffer ||
|
texOp.Type == SamplerType.TextureBuffer ||
|
||||||
texOp.Inst == Instruction.TextureSize;
|
texOp.Inst == Instruction.TextureQuerySamples ||
|
||||||
|
texOp.Inst == Instruction.TextureQuerySize;
|
||||||
|
|
||||||
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
@ -785,30 +786,31 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
|
|
||||||
private static string GetFunctionName(Operation baseOp, bool isMultiTarget, IReadOnlyList<uint> targetCbs)
|
private static string GetFunctionName(Operation baseOp, bool isMultiTarget, IReadOnlyList<uint> targetCbs)
|
||||||
{
|
{
|
||||||
string name = baseOp.Inst.ToString();
|
StringBuilder nameBuilder = new();
|
||||||
|
nameBuilder.Append(baseOp.Inst.ToString());
|
||||||
|
|
||||||
name += baseOp.StorageKind switch
|
nameBuilder.Append(baseOp.StorageKind switch
|
||||||
{
|
{
|
||||||
StorageKind.GlobalMemoryS8 => "S8",
|
StorageKind.GlobalMemoryS8 => "S8",
|
||||||
StorageKind.GlobalMemoryS16 => "S16",
|
StorageKind.GlobalMemoryS16 => "S16",
|
||||||
StorageKind.GlobalMemoryU8 => "U8",
|
StorageKind.GlobalMemoryU8 => "U8",
|
||||||
StorageKind.GlobalMemoryU16 => "U16",
|
StorageKind.GlobalMemoryU16 => "U16",
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
});
|
||||||
|
|
||||||
if (isMultiTarget)
|
if (isMultiTarget)
|
||||||
{
|
{
|
||||||
name += "Multi";
|
nameBuilder.Append("Multi");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (uint targetCb in targetCbs)
|
foreach (uint targetCb in targetCbs)
|
||||||
{
|
{
|
||||||
(int sbCbSlot, int sbCbOffset) = UnpackCbSlotAndOffset(targetCb);
|
(int sbCbSlot, int sbCbOffset) = UnpackCbSlotAndOffset(targetCb);
|
||||||
|
|
||||||
name += $"_c{sbCbSlot}o{sbCbOffset}";
|
nameBuilder.Append($"_c{sbCbSlot}o{sbCbOffset}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return nameBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryGenerateStorageOp(
|
private static bool TryGenerateStorageOp(
|
||||||
|
@ -232,8 +232,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
inst &= Instruction.Mask;
|
inst &= Instruction.Mask;
|
||||||
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
||||||
bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
||||||
bool accurateType = inst != Instruction.Lod && inst != Instruction.TextureSize;
|
bool accurateType = !inst.IsTextureQuery();
|
||||||
bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize;
|
bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureQuerySize;
|
||||||
bool coherent = flags.HasFlag(TextureFlags.Coherent);
|
bool coherent = flags.HasFlag(TextureFlags.Coherent);
|
||||||
|
|
||||||
if (!isImage)
|
if (!isImage)
|
||||||
|
@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
{
|
{
|
||||||
node = InsertCoordNormalization(context.Hfm, node, context.ResourceManager, context.GpuAccessor, context.Stage);
|
node = InsertCoordNormalization(context.Hfm, node, context.ResourceManager, context.GpuAccessor, context.Stage);
|
||||||
node = InsertCoordGatherBias(node, context.ResourceManager, context.GpuAccessor);
|
node = InsertCoordGatherBias(node, context.ResourceManager, context.GpuAccessor);
|
||||||
node = InsertConstOffsets(node, context.ResourceManager, context.GpuAccessor);
|
node = InsertConstOffsets(node, context.GpuAccessor, context.Stage);
|
||||||
|
|
||||||
if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
|
if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
|
||||||
{
|
{
|
||||||
@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
if (texOp.Inst == Instruction.TextureSize &&
|
if (texOp.Inst == Instruction.TextureQuerySize &&
|
||||||
texOp.Index < 2 &&
|
texOp.Index < 2 &&
|
||||||
!isBindless &&
|
!isBindless &&
|
||||||
!isIndexed &&
|
!isIndexed &&
|
||||||
@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
}
|
}
|
||||||
|
|
||||||
LinkedListNode<INode> textureSizeNode = node.List.AddBefore(node, new TextureOperation(
|
LinkedListNode<INode> textureSizeNode = node.List.AddBefore(node, new TextureOperation(
|
||||||
Instruction.TextureSize,
|
Instruction.TextureQuerySize,
|
||||||
texOp.Type,
|
texOp.Type,
|
||||||
texOp.Format,
|
texOp.Format,
|
||||||
texOp.Flags,
|
texOp.Flags,
|
||||||
@ -259,7 +259,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
}
|
}
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
Instruction.TextureSize,
|
Instruction.TextureQuerySize,
|
||||||
texOp.Type,
|
texOp.Type,
|
||||||
texOp.Format,
|
texOp.Format,
|
||||||
texOp.Flags,
|
texOp.Flags,
|
||||||
@ -287,7 +287,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor)
|
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, IGpuAccessor gpuAccessor, ShaderStage stage)
|
||||||
{
|
{
|
||||||
// Non-constant texture offsets are not allowed (according to the spec),
|
// Non-constant texture offsets are not allowed (according to the spec),
|
||||||
// however some GPUs does support that.
|
// however some GPUs does support that.
|
||||||
@ -440,7 +440,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
|
|
||||||
sources.CopyTo(newSources, 0);
|
sources.CopyTo(newSources, 0);
|
||||||
|
|
||||||
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount);
|
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage);
|
||||||
|
|
||||||
int destIndex = 0;
|
int destIndex = 0;
|
||||||
|
|
||||||
@ -502,7 +502,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount);
|
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage);
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
for (int index = 0; index < coordsCount; index++)
|
||||||
{
|
{
|
||||||
@ -554,11 +554,16 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
TextureOperation texOp,
|
TextureOperation texOp,
|
||||||
Operand[] lodSources,
|
Operand[] lodSources,
|
||||||
Operand bindlessHandle,
|
Operand bindlessHandle,
|
||||||
int coordsCount)
|
int coordsCount,
|
||||||
|
ShaderStage stage)
|
||||||
{
|
{
|
||||||
Operand[] texSizes = new Operand[coordsCount];
|
Operand[] texSizes = new Operand[coordsCount];
|
||||||
|
|
||||||
Operand lod = Local();
|
Operand lod;
|
||||||
|
|
||||||
|
if (stage == ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
lod = Local();
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
Instruction.Lod,
|
Instruction.Lod,
|
||||||
@ -569,6 +574,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
0,
|
0,
|
||||||
new[] { lod },
|
new[] { lod },
|
||||||
lodSources));
|
lodSources));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lod = Const(0);
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
for (int index = 0; index < coordsCount; index++)
|
||||||
{
|
{
|
||||||
@ -586,7 +596,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
}
|
}
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
Instruction.TextureSize,
|
Instruction.TextureQuerySize,
|
||||||
texOp.Type,
|
texOp.Type,
|
||||||
texOp.Format,
|
texOp.Format,
|
||||||
texOp.Flags,
|
texOp.Flags,
|
||||||
|
@ -329,6 +329,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
attributeUsage,
|
attributeUsage,
|
||||||
definitions,
|
definitions,
|
||||||
resourceManager,
|
resourceManager,
|
||||||
|
Options.TargetLanguage,
|
||||||
Options.Flags.HasFlag(TranslationFlags.DebugMode));
|
Options.Flags.HasFlag(TranslationFlags.DebugMode));
|
||||||
|
|
||||||
int geometryVerticesPerPrimitive = Definitions.OutputTopology switch
|
int geometryVerticesPerPrimitive = Definitions.OutputTopology switch
|
||||||
|
@ -19,6 +19,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem
|
namespace Ryujinx.HLE.FileSystem
|
||||||
@ -817,13 +818,13 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
if (updateNcas.Count > 0)
|
if (updateNcas.Count > 0)
|
||||||
{
|
{
|
||||||
string extraNcas = string.Empty;
|
StringBuilder extraNcas = new();
|
||||||
|
|
||||||
foreach (var entry in updateNcas)
|
foreach (var entry in updateNcas)
|
||||||
{
|
{
|
||||||
foreach (var (type, path) in entry.Value)
|
foreach (var (type, path) in entry.Value)
|
||||||
{
|
{
|
||||||
extraNcas += path + Environment.NewLine;
|
extraNcas.AppendLine(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,13 +955,13 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
if (updateNcas.Count > 0)
|
if (updateNcas.Count > 0)
|
||||||
{
|
{
|
||||||
string extraNcas = string.Empty;
|
StringBuilder extraNcas = new();
|
||||||
|
|
||||||
foreach (var entry in updateNcas)
|
foreach (var entry in updateNcas)
|
||||||
{
|
{
|
||||||
foreach (var (type, path) in entry.Value)
|
foreach (var (type, path) in entry.Value)
|
||||||
{
|
{
|
||||||
extraNcas += path + Environment.NewLine;
|
extraNcas.AppendLine(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,14 +436,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
uint nameIndex = sym.NameOffset;
|
uint nameIndex = sym.NameOffset;
|
||||||
|
|
||||||
string name = string.Empty;
|
StringBuilder nameBuilder = new();
|
||||||
|
|
||||||
for (int chr; (chr = memory.Read<byte>(strTblAddr + nameIndex++)) != 0;)
|
for (int chr; (chr = memory.Read<byte>(strTblAddr + nameIndex++)) != 0;)
|
||||||
{
|
{
|
||||||
name += (char)chr;
|
nameBuilder.Append((char)chr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
|
return new ElfSymbol(nameBuilder.ToString(), sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ElfSymbol GetSymbol32(IVirtualMemoryManager memory, ulong address, ulong strTblAddr)
|
private static ElfSymbol GetSymbol32(IVirtualMemoryManager memory, ulong address, ulong strTblAddr)
|
||||||
@ -452,14 +452,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
uint nameIndex = sym.NameOffset;
|
uint nameIndex = sym.NameOffset;
|
||||||
|
|
||||||
string name = string.Empty;
|
StringBuilder nameBuilder = new();
|
||||||
|
|
||||||
for (int chr; (chr = memory.Read<byte>(strTblAddr + nameIndex++)) != 0;)
|
for (int chr; (chr = memory.Read<byte>(strTblAddr + nameIndex++)) != 0;)
|
||||||
{
|
{
|
||||||
name += (char)chr;
|
nameBuilder.Append((char)chr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
|
return new ElfSymbol(nameBuilder.ToString(), sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|||||||
throw new InvalidOperationException("Out of handles!");
|
throw new InvalidOperationException("Out of handles!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_completionEvent.WritableEvent.Signal();
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(completionEventHandle);
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(completionEventHandle);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
@ -187,6 +189,20 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(10420)]
|
||||||
|
// nn::friends::CheckBlockedUserListAvailability(nn::account::Uid userId) -> bool
|
||||||
|
public ResultCode CheckBlockedUserListAvailability(ServiceCtx context)
|
||||||
|
{
|
||||||
|
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||||
|
|
||||||
|
// Yes, it is available.
|
||||||
|
context.ResponseData.Write(true);
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(10600)]
|
[CommandCmif(10600)]
|
||||||
// nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid userId)
|
// nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid userId)
|
||||||
public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context)
|
public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context)
|
||||||
|
@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
{
|
{
|
||||||
coreData = new CoreData();
|
coreData = new CoreData();
|
||||||
|
|
||||||
if (charInfo.IsValid())
|
if (!charInfo.IsValid())
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidCharInfo;
|
return ResultCode.InvalidCharInfo;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static Ryujinx.HLE.HOS.Services.Mii.Types.RandomMiiConstants;
|
using static Ryujinx.HLE.HOS.Services.Mii.Types.RandomMiiConstants;
|
||||||
@ -10,9 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
|||||||
{
|
{
|
||||||
public const int Size = 0x30;
|
public const int Size = 0x30;
|
||||||
|
|
||||||
private byte _storage;
|
private Array48<byte> _storage;
|
||||||
|
|
||||||
public Span<byte> Storage => MemoryMarshal.CreateSpan(ref _storage, Size);
|
public Span<byte> Storage => _storage.AsSpan();
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x18)]
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x18)]
|
||||||
public struct ElementInfo
|
public struct ElementInfo
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@ -10,12 +11,12 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
|||||||
public const int CharCount = 10;
|
public const int CharCount = 10;
|
||||||
private const int SizeConst = (CharCount + 1) * 2;
|
private const int SizeConst = (CharCount + 1) * 2;
|
||||||
|
|
||||||
private byte _storage;
|
private Array22<byte> _storage;
|
||||||
|
|
||||||
public static Nickname Default => FromString("no name");
|
public static Nickname Default => FromString("no name");
|
||||||
public static Nickname Question => FromString("???");
|
public static Nickname Question => FromString("???");
|
||||||
|
|
||||||
public Span<byte> Raw => MemoryMarshal.CreateSpan(ref _storage, SizeConst);
|
public Span<byte> Raw => _storage.AsSpan();
|
||||||
|
|
||||||
private ReadOnlySpan<ushort> Characters => MemoryMarshal.Cast<byte, ushort>(Raw);
|
private ReadOnlySpan<ushort> Characters => MemoryMarshal.Cast<byte, ushort>(Raw);
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
|||||||
|
|
||||||
private ushort CalculateDataCrc()
|
private ushort CalculateDataCrc()
|
||||||
{
|
{
|
||||||
return Helper.CalculateCrc16(AsSpanWithoutDeviceCrc(), 0, true);
|
return Helper.CalculateCrc16(AsSpanWithoutCrcs(), 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ushort CalculateDeviceCrc()
|
private ushort CalculateDeviceCrc()
|
||||||
@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
|||||||
|
|
||||||
ushort deviceIdCrc16 = Helper.CalculateCrc16(SpanHelpers.AsByteSpan(ref deviceId), 0, false);
|
ushort deviceIdCrc16 = Helper.CalculateCrc16(SpanHelpers.AsByteSpan(ref deviceId), 0, false);
|
||||||
|
|
||||||
return Helper.CalculateCrc16(AsSpan(), deviceIdCrc16, true);
|
return Helper.CalculateCrc16(AsSpanWithoutDeviceCrc(), deviceIdCrc16, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlySpan<byte> AsSpan()
|
private ReadOnlySpan<byte> AsSpan()
|
||||||
@ -84,6 +84,11 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
|||||||
return AsSpan()[..(Size - 2)];
|
return AsSpan()[..(Size - 2)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> AsSpanWithoutCrcs()
|
||||||
|
{
|
||||||
|
return AsSpan()[..(Size - 4)];
|
||||||
|
}
|
||||||
|
|
||||||
public static StoreData BuildDefault(UtilityImpl utilImpl, uint index)
|
public static StoreData BuildDefault(UtilityImpl utilImpl, uint index)
|
||||||
{
|
{
|
||||||
StoreData result = new()
|
StoreData result = new()
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
@ -8,9 +10,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
|||||||
{
|
{
|
||||||
public const int Size = 0x60;
|
public const int Size = 0x60;
|
||||||
|
|
||||||
private byte _storage;
|
private Array96<byte> _storage;
|
||||||
|
|
||||||
public Span<byte> Storage => MemoryMarshal.CreateSpan(ref _storage, Size);
|
public Span<byte> Storage => _storage.AsSpan();
|
||||||
|
|
||||||
// TODO: define all getters/setters
|
// TODO: define all getters/setters
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Sm
|
namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
{
|
{
|
||||||
@ -235,7 +236,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
|
|
||||||
private static string ReadName(ServiceCtx context)
|
private static string ReadName(ServiceCtx context)
|
||||||
{
|
{
|
||||||
string name = string.Empty;
|
StringBuilder nameBuilder = new();
|
||||||
|
|
||||||
for (int index = 0; index < 8 &&
|
for (int index = 0; index < 8 &&
|
||||||
context.RequestData.BaseStream.Position <
|
context.RequestData.BaseStream.Position <
|
||||||
@ -245,11 +246,11 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
|||||||
|
|
||||||
if (chr >= 0x20 && chr < 0x7f)
|
if (chr >= 0x20 && chr < 0x7f)
|
||||||
{
|
{
|
||||||
name += (char)chr;
|
nameBuilder.Append((char)chr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return nameBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DestroyAtExit()
|
public override void DestroyAtExit()
|
||||||
|
@ -669,6 +669,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
lock (Core.Lock)
|
lock (Core.Lock)
|
||||||
{
|
{
|
||||||
|
// If we are replacing a buffer that has already been queued, make sure we release the references.
|
||||||
|
if (Core.Slots[slot].BufferState == BufferState.Queued)
|
||||||
|
{
|
||||||
|
Core.Slots[slot].GraphicBuffer.Object.DecrementNvMapHandleRefCount(Core.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
Core.Slots[slot].BufferState = BufferState.Free;
|
Core.Slots[slot].BufferState = BufferState.Free;
|
||||||
Core.Slots[slot].Fence = AndroidFence.NoFence;
|
Core.Slots[slot].Fence = AndroidFence.NoFence;
|
||||||
Core.Slots[slot].RequestBufferCalled = false;
|
Core.Slots[slot].RequestBufferCalled = false;
|
||||||
|
@ -142,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
// OpenDisplay(nn::vi::DisplayName) -> u64 display_id
|
// OpenDisplay(nn::vi::DisplayName) -> u64 display_id
|
||||||
public ResultCode OpenDisplay(ServiceCtx context)
|
public ResultCode OpenDisplay(ServiceCtx context)
|
||||||
{
|
{
|
||||||
string name = "";
|
StringBuilder nameBuilder = new();
|
||||||
|
|
||||||
for (int index = 0; index < 8 && context.RequestData.BaseStream.Position < context.RequestData.BaseStream.Length; index++)
|
for (int index = 0; index < 8 && context.RequestData.BaseStream.Position < context.RequestData.BaseStream.Length; index++)
|
||||||
{
|
{
|
||||||
@ -150,11 +150,11 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
|
|
||||||
if (chr >= 0x20 && chr < 0x7f)
|
if (chr >= 0x20 && chr < 0x7f)
|
||||||
{
|
{
|
||||||
name += (char)chr;
|
nameBuilder.Append((char)chr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return OpenDisplayImpl(context, name);
|
return OpenDisplayImpl(context, nameBuilder.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandCmif(1011)]
|
[CommandCmif(1011)]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon.Sdk.Sm
|
namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
{
|
{
|
||||||
@ -78,7 +79,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
string name = string.Empty;
|
StringBuilder nameBuilder = new();
|
||||||
|
|
||||||
for (int index = 0; index < sizeof(ulong); index++)
|
for (int index = 0; index < sizeof(ulong); index++)
|
||||||
{
|
{
|
||||||
@ -89,10 +90,10 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
name += (char)character;
|
nameBuilder.Append((char)character);
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return nameBuilder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.LocaleGenerator
|
namespace Ryujinx.Ui.LocaleGenerator
|
||||||
{
|
{
|
||||||
@ -15,15 +16,17 @@ namespace Ryujinx.Ui.LocaleGenerator
|
|||||||
context.RegisterSourceOutput(contents, (spc, content) =>
|
context.RegisterSourceOutput(contents, (spc, content) =>
|
||||||
{
|
{
|
||||||
var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"")).Select(x => x.Split(':')[0].Trim().Replace("\"", ""));
|
var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"")).Select(x => x.Split(':')[0].Trim().Replace("\"", ""));
|
||||||
string enumSource = "namespace Ryujinx.Ava.Common.Locale;\n";
|
StringBuilder enumSourceBuilder = new();
|
||||||
enumSource += "internal enum LocaleKeys\n{\n";
|
enumSourceBuilder.AppendLine("namespace Ryujinx.Ava.Common.Locale;");
|
||||||
|
enumSourceBuilder.AppendLine("internal enum LocaleKeys");
|
||||||
|
enumSourceBuilder.AppendLine("{");
|
||||||
foreach (var line in lines)
|
foreach (var line in lines)
|
||||||
{
|
{
|
||||||
enumSource += $" {line},\n";
|
enumSourceBuilder.AppendLine($" {line},");
|
||||||
}
|
}
|
||||||
enumSource += "}\n";
|
enumSourceBuilder.AppendLine("}");
|
||||||
|
|
||||||
spc.AddSource("LocaleKeys", enumSource);
|
spc.AddSource("LocaleKeys", enumSourceBuilder.ToString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user