Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5b42a4d2c4 | ||
|
8f0c89ffd6 | ||
|
2c9715acf6 | ||
|
274af65f69 | ||
|
4ca78eded5 | ||
|
6cb6b15612 | ||
|
2725e40838 | ||
|
c2e4c8f98e | ||
|
b53e7ffd46 | ||
|
ac66643346 | ||
|
21e88f17f6 | ||
|
5626f2ca1c |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create tag
|
- name: Create tag
|
||||||
uses: actions/github-script@v5
|
uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.git.createRef({
|
github.rest.git.createRef({
|
||||||
|
@@ -216,26 +216,17 @@
|
|||||||
"ControllerSettingsDPadDown": "Down",
|
"ControllerSettingsDPadDown": "Down",
|
||||||
"ControllerSettingsDPadLeft": "Left",
|
"ControllerSettingsDPadLeft": "Left",
|
||||||
"ControllerSettingsDPadRight": "Right",
|
"ControllerSettingsDPadRight": "Right",
|
||||||
|
"ControllerSettingsStickButton": "Button",
|
||||||
|
"ControllerSettingsStickUp": "Up",
|
||||||
|
"ControllerSettingsStickDown": "Down",
|
||||||
|
"ControllerSettingsStickLeft": "Left",
|
||||||
|
"ControllerSettingsStickRight": "Right",
|
||||||
|
"ControllerSettingsStickStick": "Stick",
|
||||||
|
"ControllerSettingsStickInvertXAxis": "Invert Stick X",
|
||||||
|
"ControllerSettingsStickInvertYAxis": "Invert Stick Y",
|
||||||
|
"ControllerSettingsStickDeadzone": "Deadzone:",
|
||||||
"ControllerSettingsLStick": "Left Stick",
|
"ControllerSettingsLStick": "Left Stick",
|
||||||
"ControllerSettingsLStickButton": "Button",
|
|
||||||
"ControllerSettingsLStickUp": "Up",
|
|
||||||
"ControllerSettingsLStickDown": "Down",
|
|
||||||
"ControllerSettingsLStickLeft": "Left",
|
|
||||||
"ControllerSettingsLStickRight": "Right",
|
|
||||||
"ControllerSettingsLStickStick": "Stick",
|
|
||||||
"ControllerSettingsLStickInvertXAxis": "Invert Stick X",
|
|
||||||
"ControllerSettingsLStickInvertYAxis": "Invert Stick Y",
|
|
||||||
"ControllerSettingsLStickDeadzone": "Deadzone:",
|
|
||||||
"ControllerSettingsRStick": "Right Stick",
|
"ControllerSettingsRStick": "Right Stick",
|
||||||
"ControllerSettingsRStickButton": "Button",
|
|
||||||
"ControllerSettingsRStickUp": "Up",
|
|
||||||
"ControllerSettingsRStickDown": "Down",
|
|
||||||
"ControllerSettingsRStickLeft": "Left",
|
|
||||||
"ControllerSettingsRStickRight": "Right",
|
|
||||||
"ControllerSettingsRStickStick": "Stick",
|
|
||||||
"ControllerSettingsRStickInvertXAxis": "Invert Stick X",
|
|
||||||
"ControllerSettingsRStickInvertYAxis": "Invert Stick Y",
|
|
||||||
"ControllerSettingsRStickDeadzone": "Deadzone:",
|
|
||||||
"ControllerSettingsTriggersLeft": "Triggers Left",
|
"ControllerSettingsTriggersLeft": "Triggers Left",
|
||||||
"ControllerSettingsTriggersRight": "Triggers Right",
|
"ControllerSettingsTriggersRight": "Triggers Right",
|
||||||
"ControllerSettingsTriggersButtonsLeft": "Trigger Buttons Left",
|
"ControllerSettingsTriggersButtonsLeft": "Trigger Buttons Left",
|
||||||
|
@@ -7,6 +7,7 @@ using Ryujinx.Ava.Common.Locale;
|
|||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
|
using Ryujinx.Ava.UI.Views.Input;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
@@ -30,7 +31,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key;
|
|||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
public class ControllerSettingsViewModel : BaseModel, IDisposable
|
public class ControllerInputViewModel : BaseModel, IDisposable
|
||||||
{
|
{
|
||||||
private const string Disabled = "disabled";
|
private const string Disabled = "disabled";
|
||||||
private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg";
|
private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg";
|
||||||
@@ -231,7 +232,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public InputConfig Config { get; set; }
|
public InputConfig Config { get; set; }
|
||||||
|
|
||||||
public ControllerSettingsViewModel(UserControl owner) : this()
|
public ControllerInputViewModel(UserControl owner) : this()
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
|
|
||||||
@@ -258,7 +259,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControllerSettingsViewModel()
|
public ControllerInputViewModel()
|
||||||
{
|
{
|
||||||
PlayerIndexes = new ObservableCollection<PlayerModel>();
|
PlayerIndexes = new ObservableCollection<PlayerModel>();
|
||||||
Controllers = new ObservableCollection<ControllerModel>();
|
Controllers = new ObservableCollection<ControllerModel>();
|
||||||
@@ -328,12 +329,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public async void ShowMotionConfig()
|
public async void ShowMotionConfig()
|
||||||
{
|
{
|
||||||
await MotionSettingsWindow.Show(this);
|
await MotionInputView.Show(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ShowRumbleConfig()
|
public async void ShowRumbleConfig()
|
||||||
{
|
{
|
||||||
await RumbleSettingsWindow.Show(this);
|
await RumbleInputView.Show(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadInputDriver()
|
private void LoadInputDriver()
|
93
src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs
Normal file
93
src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class MotionInputViewModel : BaseModel
|
||||||
|
{
|
||||||
|
private int _slot;
|
||||||
|
public int Slot
|
||||||
|
{
|
||||||
|
get => _slot;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_slot = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _altSlot;
|
||||||
|
public int AltSlot
|
||||||
|
{
|
||||||
|
get => _altSlot;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_altSlot = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _dsuServerHost;
|
||||||
|
public string DsuServerHost
|
||||||
|
{
|
||||||
|
get => _dsuServerHost;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_dsuServerHost = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _dsuServerPort;
|
||||||
|
public int DsuServerPort
|
||||||
|
{
|
||||||
|
get => _dsuServerPort;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_dsuServerPort = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _mirrorInput;
|
||||||
|
public bool MirrorInput
|
||||||
|
{
|
||||||
|
get => _mirrorInput;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_mirrorInput = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _sensitivity;
|
||||||
|
public int Sensitivity
|
||||||
|
{
|
||||||
|
get => _sensitivity;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_sensitivity = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double _gryoDeadzone;
|
||||||
|
public double GyroDeadzone
|
||||||
|
{
|
||||||
|
get => _gryoDeadzone;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_gryoDeadzone = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _enableCemuHookMotion;
|
||||||
|
public bool EnableCemuHookMotion
|
||||||
|
{
|
||||||
|
get => _enableCemuHookMotion;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_enableCemuHookMotion = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs
Normal file
27
src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class RumbleInputViewModel : BaseModel
|
||||||
|
{
|
||||||
|
private float _strongRumble;
|
||||||
|
public float StrongRumble
|
||||||
|
{
|
||||||
|
get => _strongRumble;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_strongRumble = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float _weakRumble;
|
||||||
|
public float WeakRumble
|
||||||
|
{
|
||||||
|
get => _weakRumble;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_weakRumble = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -311,7 +311,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
_gpuIds = new List<string>();
|
_gpuIds = new List<string>();
|
||||||
List<string> names = new();
|
List<string> names = new();
|
||||||
var devices = VulkanRenderer.GetPhysicalDevices(Vk.GetApi());
|
var devices = VulkanRenderer.GetPhysicalDevices();
|
||||||
|
|
||||||
if (devices.Length == 0)
|
if (devices.Length == 0)
|
||||||
{
|
{
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@ using Avalonia.Input;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
@@ -13,18 +12,18 @@ using Ryujinx.Input;
|
|||||||
using Ryujinx.Input.Assigner;
|
using Ryujinx.Input.Assigner;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Views.Input
|
||||||
{
|
{
|
||||||
public partial class ControllerSettingsWindow : UserControl
|
public partial class ControllerInputView : UserControl
|
||||||
{
|
{
|
||||||
private bool _dialogOpen;
|
private bool _dialogOpen;
|
||||||
|
|
||||||
private ButtonKeyAssigner _currentAssigner;
|
private ButtonKeyAssigner _currentAssigner;
|
||||||
internal ControllerSettingsViewModel ViewModel { get; set; }
|
internal ControllerInputViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
public ControllerSettingsWindow()
|
public ControllerInputView()
|
||||||
{
|
{
|
||||||
DataContext = ViewModel = new ControllerSettingsViewModel(this);
|
DataContext = ViewModel = new ControllerInputViewModel(this);
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
@@ -5,8 +5,11 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:Class="Ryujinx.Ava.UI.Windows.MotionSettingsWindow"
|
x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView"
|
||||||
|
x:CompileBindings="True"
|
||||||
|
x:DataType="viewModels:MotionInputViewModel"
|
||||||
Focusable="True">
|
Focusable="True">
|
||||||
<Grid Margin="10">
|
<Grid Margin="10">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -14,7 +17,9 @@
|
|||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
<StackPanel
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@@ -28,11 +33,14 @@
|
|||||||
Maximum="100"
|
Maximum="100"
|
||||||
Minimum="0"
|
Minimum="0"
|
||||||
Value="{Binding Sensitivity, Mode=TwoWay}" />
|
Value="{Binding Sensitivity, Mode=TwoWay}" />
|
||||||
<TextBlock HorizontalAlignment="Center"
|
<TextBlock
|
||||||
Margin="5, 0"
|
HorizontalAlignment="Center"
|
||||||
Text="{Binding Sensitivity, StringFormat=\{0:0\}%}" />
|
Margin="5, 0"
|
||||||
|
Text="{Binding Sensitivity, StringFormat=\{0:0\}%}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
<StackPanel
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@@ -51,17 +59,25 @@
|
|||||||
Margin="5, 0"
|
Margin="5, 0"
|
||||||
Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" />
|
Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Separator Height="1" Margin="0,5" />
|
<Separator
|
||||||
<CheckBox Margin="5" IsChecked="{Binding EnableCemuHookMotion}">
|
Height="1"
|
||||||
<TextBlock Margin="0,3,0,0" VerticalAlignment="Center"
|
Margin="0,5" />
|
||||||
Text="{locale:Locale ControllerSettingsMotionUseCemuhookCompatibleMotion}" />
|
<CheckBox
|
||||||
|
Margin="5"
|
||||||
|
IsChecked="{Binding EnableCemuHookMotion}">
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,3,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{locale:Locale ControllerSettingsMotionUseCemuhookCompatibleMotion}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Border Grid.Row="1"
|
<Border
|
||||||
Padding="20,5"
|
Grid.Row="1"
|
||||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
Padding="20,5"
|
||||||
BorderThickness="1"
|
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||||
HorizontalAlignment="Stretch">
|
BorderThickness="1"
|
||||||
|
CornerRadius="5"
|
||||||
|
HorizontalAlignment="Stretch">
|
||||||
<Grid VerticalAlignment="Top">
|
<Grid VerticalAlignment="Top">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
@@ -109,30 +125,42 @@
|
|||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Margin="0,10,0,0" VerticalAlignment="Center"
|
<TextBlock
|
||||||
Text="{locale:Locale ControllerSettingsMotionControllerSlot}" />
|
Margin="0,10,0,0"
|
||||||
<ui:NumberBox Grid.Row="0" Grid.Column="1"
|
VerticalAlignment="Center"
|
||||||
Name="CemuHookSlotUpDown"
|
Text="{locale:Locale ControllerSettingsMotionControllerSlot}" />
|
||||||
SmallChange="1"
|
<ui:NumberBox
|
||||||
LargeChange="1"
|
Grid.Row="0"
|
||||||
Maximum="4"
|
Grid.Column="1"
|
||||||
Minimum="0"
|
Name="CemuHookSlotUpDown"
|
||||||
Value="{Binding Slot}" />
|
SmallChange="1"
|
||||||
<TextBlock Margin="0,10,0,0" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
|
LargeChange="1"
|
||||||
Text="{locale:Locale ControllerSettingsMotionRightJoyConSlot}" />
|
Maximum="4"
|
||||||
<ui:NumberBox Grid.Row="1" Grid.Column="1"
|
Minimum="0"
|
||||||
Name="CemuHookRightJoyConSlotUpDown"
|
Value="{Binding Slot}" />
|
||||||
SmallChange="1"
|
<TextBlock
|
||||||
LargeChange="1"
|
Margin="0,10,0,0"
|
||||||
Maximum="4"
|
Grid.Row="1"
|
||||||
Minimum="0"
|
Grid.Column="0"
|
||||||
Value="{Binding AltSlot}" />
|
VerticalAlignment="Center"
|
||||||
|
Text="{locale:Locale ControllerSettingsMotionRightJoyConSlot}" />
|
||||||
|
<ui:NumberBox
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Name="CemuHookRightJoyConSlotUpDown"
|
||||||
|
SmallChange="1"
|
||||||
|
LargeChange="1"
|
||||||
|
Maximum="4"
|
||||||
|
Minimum="0"
|
||||||
|
Value="{Binding AltSlot}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<CheckBox HorizontalAlignment="Center"
|
<CheckBox
|
||||||
IsChecked="{Binding MirrorInput, Mode=TwoWay}">
|
HorizontalAlignment="Center"
|
||||||
<TextBlock HorizontalAlignment="Center"
|
IsChecked="{Binding MirrorInput, Mode=TwoWay}">
|
||||||
Text="{locale:Locale ControllerSettingsMotionMirrorInput}" />
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Text="{locale:Locale ControllerSettingsMotionMirrorInput}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
@@ -6,44 +6,42 @@ using Ryujinx.Ava.UI.ViewModels;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Views.Input
|
||||||
{
|
{
|
||||||
public partial class MotionSettingsWindow : UserControl
|
public partial class MotionInputView : UserControl
|
||||||
{
|
{
|
||||||
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
|
private MotionInputViewModel _viewModel;
|
||||||
|
|
||||||
public MotionSettingsWindow()
|
public MotionInputView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _viewmodel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MotionSettingsWindow(ControllerSettingsViewModel viewmodel)
|
public MotionInputView(ControllerInputViewModel viewModel)
|
||||||
{
|
{
|
||||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||||
|
|
||||||
_viewmodel = new InputConfiguration<GamepadInputId, StickInputId>()
|
_viewModel = new MotionInputViewModel
|
||||||
{
|
{
|
||||||
Slot = config.Slot,
|
Slot = config.Slot,
|
||||||
AltSlot = config.AltSlot,
|
AltSlot = config.AltSlot,
|
||||||
DsuServerHost = config.DsuServerHost,
|
DsuServerHost = config.DsuServerHost,
|
||||||
DsuServerPort = config.DsuServerPort,
|
DsuServerPort = config.DsuServerPort,
|
||||||
MirrorInput = config.MirrorInput,
|
MirrorInput = config.MirrorInput,
|
||||||
EnableMotion = config.EnableMotion,
|
|
||||||
Sensitivity = config.Sensitivity,
|
Sensitivity = config.Sensitivity,
|
||||||
GyroDeadzone = config.GyroDeadzone,
|
GyroDeadzone = config.GyroDeadzone,
|
||||||
EnableCemuHookMotion = config.EnableCemuHookMotion
|
EnableCemuHookMotion = config.EnableCemuHookMotion
|
||||||
};
|
};
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _viewmodel;
|
DataContext = _viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Show(ControllerSettingsViewModel viewmodel)
|
public static async Task Show(ControllerInputViewModel viewModel)
|
||||||
{
|
{
|
||||||
MotionSettingsWindow content = new MotionSettingsWindow(viewmodel);
|
MotionInputView content = new(viewModel);
|
||||||
|
|
||||||
ContentDialog contentDialog = new ContentDialog
|
ContentDialog contentDialog = new()
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.ControllerMotionTitle],
|
Title = LocaleManager.Instance[LocaleKeys.ControllerMotionTitle],
|
||||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
||||||
@@ -53,16 +51,15 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
};
|
};
|
||||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||||
{
|
{
|
||||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||||
config.Slot = content._viewmodel.Slot;
|
config.Slot = content._viewModel.Slot;
|
||||||
config.EnableMotion = content._viewmodel.EnableMotion;
|
config.Sensitivity = content._viewModel.Sensitivity;
|
||||||
config.Sensitivity = content._viewmodel.Sensitivity;
|
config.GyroDeadzone = content._viewModel.GyroDeadzone;
|
||||||
config.GyroDeadzone = content._viewmodel.GyroDeadzone;
|
config.AltSlot = content._viewModel.AltSlot;
|
||||||
config.AltSlot = content._viewmodel.AltSlot;
|
config.DsuServerHost = content._viewModel.DsuServerHost;
|
||||||
config.DsuServerHost = content._viewmodel.DsuServerHost;
|
config.DsuServerPort = content._viewModel.DsuServerPort;
|
||||||
config.DsuServerPort = content._viewmodel.DsuServerPort;
|
config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion;
|
||||||
config.EnableCemuHookMotion = content._viewmodel.EnableCemuHookMotion;
|
config.MirrorInput = content._viewModel.MirrorInput;
|
||||||
config.MirrorInput = content._viewmodel.MirrorInput;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await contentDialog.ShowAsync();
|
await contentDialog.ShowAsync();
|
@@ -4,8 +4,11 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:Class="Ryujinx.Ava.UI.Windows.RumbleSettingsWindow"
|
x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView"
|
||||||
|
x:DataType="viewModels:RumbleInputViewModel"
|
||||||
|
x:CompileBindings="True"
|
||||||
Focusable="True">
|
Focusable="True">
|
||||||
<Grid Margin="10">
|
<Grid Margin="10">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
@@ -6,36 +6,37 @@ using Ryujinx.Ava.UI.ViewModels;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Views.Input
|
||||||
{
|
{
|
||||||
public partial class RumbleSettingsWindow : UserControl
|
public partial class RumbleInputView : UserControl
|
||||||
{
|
{
|
||||||
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
|
private RumbleInputViewModel _viewModel;
|
||||||
|
|
||||||
public RumbleSettingsWindow()
|
public RumbleInputView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _viewmodel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RumbleSettingsWindow(ControllerSettingsViewModel viewmodel)
|
public RumbleInputView(ControllerInputViewModel viewModel)
|
||||||
{
|
{
|
||||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||||
|
|
||||||
_viewmodel = new InputConfiguration<GamepadInputId, StickInputId>()
|
_viewModel = new RumbleInputViewModel
|
||||||
{
|
{
|
||||||
StrongRumble = config.StrongRumble, WeakRumble = config.WeakRumble
|
StrongRumble = config.StrongRumble,
|
||||||
|
WeakRumble = config.WeakRumble
|
||||||
};
|
};
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _viewmodel;
|
|
||||||
|
DataContext = _viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Show(ControllerSettingsViewModel viewmodel)
|
public static async Task Show(ControllerInputViewModel viewModel)
|
||||||
{
|
{
|
||||||
RumbleSettingsWindow content = new RumbleSettingsWindow(viewmodel);
|
RumbleInputView content = new(viewModel);
|
||||||
|
|
||||||
ContentDialog contentDialog = new ContentDialog
|
ContentDialog contentDialog = new()
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.ControllerRumbleTitle],
|
Title = LocaleManager.Instance[LocaleKeys.ControllerRumbleTitle],
|
||||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
||||||
@@ -46,9 +47,9 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||||
{
|
{
|
||||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||||
config.StrongRumble = content._viewmodel.StrongRumble;
|
config.StrongRumble = content._viewModel.StrongRumble;
|
||||||
config.WeakRumble = content._viewmodel.WeakRumble;
|
config.WeakRumble = content._viewModel.WeakRumble;
|
||||||
};
|
};
|
||||||
|
|
||||||
await contentDialog.ShowAsync();
|
await contentDialog.ShowAsync();
|
@@ -5,7 +5,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
xmlns:views="clr-namespace:Ryujinx.Ava.UI.Views.Input"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:CompileBindings="True"
|
x:CompileBindings="True"
|
||||||
@@ -20,27 +20,49 @@
|
|||||||
HorizontalScrollBarVisibility="Disabled"
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
VerticalScrollBarVisibility="Auto">
|
VerticalScrollBarVisibility="Auto">
|
||||||
<Border Classes="settings">
|
<Border Classes="settings">
|
||||||
<StackPanel Margin="4" Orientation="Vertical">
|
<Panel
|
||||||
<StackPanel Orientation="Horizontal">
|
Margin="10">
|
||||||
<CheckBox Margin="5,0"
|
<Grid>
|
||||||
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
|
<Grid.RowDefinitions>
|
||||||
IsChecked="{Binding EnableDockedMode}">
|
<RowDefinition Height="Auto"/>
|
||||||
<TextBlock VerticalAlignment="Center"
|
<RowDefinition Height="*" />
|
||||||
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
|
<RowDefinition Height="Auto" />
|
||||||
</CheckBox>
|
</Grid.RowDefinitions>
|
||||||
<CheckBox Margin="5,0"
|
<views:ControllerInputView
|
||||||
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
|
Grid.Row="0"
|
||||||
IsChecked="{Binding EnableKeyboard}">
|
Name="ControllerSettings" />
|
||||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
|
<StackPanel
|
||||||
</CheckBox>
|
Orientation="Vertical"
|
||||||
<CheckBox Margin="5,0"
|
Grid.Row="2">
|
||||||
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
|
<Separator
|
||||||
IsChecked="{Binding EnableMouse}">
|
Margin="0 10"
|
||||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
|
Height="1" />
|
||||||
</CheckBox>
|
<StackPanel
|
||||||
</StackPanel>
|
Orientation="Horizontal"
|
||||||
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0" MinHeight="600" />
|
Spacing="10">
|
||||||
</StackPanel>
|
<CheckBox
|
||||||
|
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
|
||||||
|
MinWidth="0"
|
||||||
|
IsChecked="{Binding EnableDockedMode}">
|
||||||
|
<TextBlock
|
||||||
|
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox
|
||||||
|
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
|
||||||
|
IsChecked="{Binding EnableKeyboard}">
|
||||||
|
<TextBlock
|
||||||
|
Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox
|
||||||
|
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
|
||||||
|
IsChecked="{Binding EnableMouse}">
|
||||||
|
<TextBlock
|
||||||
|
Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
|
||||||
|
</CheckBox>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Panel>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
179
src/Ryujinx.Graphics.GAL/ResourceLayout.cs
Normal file
179
src/Ryujinx.Graphics.GAL/ResourceLayout.cs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum ResourceType : byte
|
||||||
|
{
|
||||||
|
UniformBuffer,
|
||||||
|
StorageBuffer,
|
||||||
|
Texture,
|
||||||
|
Sampler,
|
||||||
|
TextureAndSampler,
|
||||||
|
Image,
|
||||||
|
BufferTexture,
|
||||||
|
BufferImage
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ResourceAccess : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Read = 1,
|
||||||
|
Write = 2,
|
||||||
|
ReadWrite = Read | Write
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum ResourceStages : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Compute = 1 << 0,
|
||||||
|
Vertex = 1 << 1,
|
||||||
|
TessellationControl = 1 << 2,
|
||||||
|
TessellationEvaluation = 1 << 3,
|
||||||
|
Geometry = 1 << 4,
|
||||||
|
Fragment = 1 << 5
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct ResourceDescriptor : IEquatable<ResourceDescriptor>
|
||||||
|
{
|
||||||
|
public int Binding { get; }
|
||||||
|
public int Count { get; }
|
||||||
|
public ResourceType Type { get; }
|
||||||
|
public ResourceStages Stages { get; }
|
||||||
|
|
||||||
|
public ResourceDescriptor(int binding, int count, ResourceType type, ResourceStages stages)
|
||||||
|
{
|
||||||
|
Binding = binding;
|
||||||
|
Count = count;
|
||||||
|
Type = type;
|
||||||
|
Stages = stages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Binding, Count, Type, Stages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is ResourceDescriptor other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ResourceDescriptor other)
|
||||||
|
{
|
||||||
|
return Binding == other.Binding && Count == other.Count && Type == other.Type && Stages == other.Stages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct ResourceUsage : IEquatable<ResourceUsage>
|
||||||
|
{
|
||||||
|
public int Binding { get; }
|
||||||
|
public ResourceType Type { get; }
|
||||||
|
public ResourceStages Stages { get; }
|
||||||
|
public ResourceAccess Access { get; }
|
||||||
|
|
||||||
|
public ResourceUsage(int binding, ResourceType type, ResourceStages stages, ResourceAccess access)
|
||||||
|
{
|
||||||
|
Binding = binding;
|
||||||
|
Type = type;
|
||||||
|
Stages = stages;
|
||||||
|
Access = access;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Binding, Type, Stages, Access);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is ResourceUsage other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ResourceUsage other)
|
||||||
|
{
|
||||||
|
return Binding == other.Binding && Type == other.Type && Stages == other.Stages && Access == other.Access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct ResourceDescriptorCollection
|
||||||
|
{
|
||||||
|
public ReadOnlyCollection<ResourceDescriptor> Descriptors { get; }
|
||||||
|
|
||||||
|
public ResourceDescriptorCollection(ReadOnlyCollection<ResourceDescriptor> descriptors)
|
||||||
|
{
|
||||||
|
Descriptors = descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hasher = new HashCode();
|
||||||
|
|
||||||
|
if (Descriptors != null)
|
||||||
|
{
|
||||||
|
foreach (var descriptor in Descriptors)
|
||||||
|
{
|
||||||
|
hasher.Add(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasher.ToHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is ResourceDescriptorCollection other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ResourceDescriptorCollection other)
|
||||||
|
{
|
||||||
|
if ((Descriptors == null) != (other.Descriptors == null))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Descriptors != null)
|
||||||
|
{
|
||||||
|
if (Descriptors.Count != other.Descriptors.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < Descriptors.Count; index++)
|
||||||
|
{
|
||||||
|
if (!Descriptors[index].Equals(other.Descriptors[index]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct ResourceUsageCollection
|
||||||
|
{
|
||||||
|
public ReadOnlyCollection<ResourceUsage> Usages { get; }
|
||||||
|
|
||||||
|
public ResourceUsageCollection(ReadOnlyCollection<ResourceUsage> usages)
|
||||||
|
{
|
||||||
|
Usages = usages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct ResourceLayout
|
||||||
|
{
|
||||||
|
public ReadOnlyCollection<ResourceDescriptorCollection> Sets { get; }
|
||||||
|
public ReadOnlyCollection<ResourceUsageCollection> SetUsages { get; }
|
||||||
|
|
||||||
|
public ResourceLayout(
|
||||||
|
ReadOnlyCollection<ResourceDescriptorCollection> sets,
|
||||||
|
ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
||||||
|
{
|
||||||
|
Sets = sets;
|
||||||
|
SetUsages = setUsages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,24 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
|
||||||
{
|
|
||||||
public readonly struct ShaderBindings
|
|
||||||
{
|
|
||||||
public IReadOnlyCollection<int> UniformBufferBindings { get; }
|
|
||||||
public IReadOnlyCollection<int> StorageBufferBindings { get; }
|
|
||||||
public IReadOnlyCollection<int> TextureBindings { get; }
|
|
||||||
public IReadOnlyCollection<int> ImageBindings { get; }
|
|
||||||
|
|
||||||
public ShaderBindings(
|
|
||||||
IReadOnlyCollection<int> uniformBufferBindings,
|
|
||||||
IReadOnlyCollection<int> storageBufferBindings,
|
|
||||||
IReadOnlyCollection<int> textureBindings,
|
|
||||||
IReadOnlyCollection<int> imageBindings)
|
|
||||||
{
|
|
||||||
UniformBufferBindings = uniformBufferBindings;
|
|
||||||
StorageBufferBindings = storageBufferBindings;
|
|
||||||
TextureBindings = textureBindings;
|
|
||||||
ImageBindings = imageBindings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,19 +3,22 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public struct ShaderInfo
|
public struct ShaderInfo
|
||||||
{
|
{
|
||||||
public int FragmentOutputMap { get; }
|
public int FragmentOutputMap { get; }
|
||||||
|
public ResourceLayout ResourceLayout { get; }
|
||||||
public ProgramPipelineState? State { get; }
|
public ProgramPipelineState? State { get; }
|
||||||
public bool FromCache { get; set; }
|
public bool FromCache { get; set; }
|
||||||
|
|
||||||
public ShaderInfo(int fragmentOutputMap, ProgramPipelineState state, bool fromCache = false)
|
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false)
|
||||||
{
|
{
|
||||||
FragmentOutputMap = fragmentOutputMap;
|
FragmentOutputMap = fragmentOutputMap;
|
||||||
|
ResourceLayout = resourceLayout;
|
||||||
State = state;
|
State = state;
|
||||||
FromCache = fromCache;
|
FromCache = fromCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderInfo(int fragmentOutputMap, bool fromCache = false)
|
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false)
|
||||||
{
|
{
|
||||||
FragmentOutputMap = fragmentOutputMap;
|
FragmentOutputMap = fragmentOutputMap;
|
||||||
|
ResourceLayout = resourceLayout;
|
||||||
State = null;
|
State = null;
|
||||||
FromCache = fromCache;
|
FromCache = fromCache;
|
||||||
}
|
}
|
||||||
|
@@ -7,24 +7,22 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
{
|
{
|
||||||
public string Code { get; }
|
public string Code { get; }
|
||||||
public byte[] BinaryCode { get; }
|
public byte[] BinaryCode { get; }
|
||||||
public ShaderBindings Bindings { get; }
|
|
||||||
public ShaderStage Stage { get; }
|
public ShaderStage Stage { get; }
|
||||||
public TargetLanguage Language { get; }
|
public TargetLanguage Language { get; }
|
||||||
|
|
||||||
public ShaderSource(string code, byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language)
|
public ShaderSource(string code, byte[] binaryCode, ShaderStage stage, TargetLanguage language)
|
||||||
{
|
{
|
||||||
Code = code;
|
Code = code;
|
||||||
BinaryCode = binaryCode;
|
BinaryCode = binaryCode;
|
||||||
Bindings = bindings;
|
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Language = language;
|
Language = language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderSource(string code, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(code, null, bindings, stage, language)
|
public ShaderSource(string code, ShaderStage stage, TargetLanguage language) : this(code, null, stage, language)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderSource(byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, bindings, stage, language)
|
public ShaderSource(byte[] binaryCode, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, stage, language)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 = 4646;
|
private const uint CodeGenVersion = 4714;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
@@ -368,12 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
|
|
||||||
if (hostCode != null)
|
if (hostCode != null)
|
||||||
{
|
{
|
||||||
bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
|
ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(context, shaders, specState.PipelineState);
|
||||||
int fragmentOutputMap = hasFragmentShader ? shaders[5].Info.FragmentOutputMap : -1;
|
|
||||||
|
|
||||||
ShaderInfo shaderInfo = specState.PipelineState.HasValue
|
|
||||||
? new ShaderInfo(fragmentOutputMap, specState.PipelineState.Value, fromCache: true)
|
|
||||||
: new ShaderInfo(fragmentOutputMap, fromCache: true);
|
|
||||||
|
|
||||||
IProgram hostProgram;
|
IProgram hostProgram;
|
||||||
|
|
||||||
@@ -385,6 +380,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
|
||||||
|
|
||||||
hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
|
hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -491,23 +491,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
{
|
{
|
||||||
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
|
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
|
||||||
|
|
||||||
int fragmentOutputMap = -1;
|
ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context);
|
||||||
|
|
||||||
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
|
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
|
||||||
{
|
{
|
||||||
ShaderProgram shader = compilation.TranslatedStages[index];
|
ShaderProgram shader = compilation.TranslatedStages[index];
|
||||||
shaderSources[index] = CreateShaderSource(shader);
|
shaderSources[index] = CreateShaderSource(shader);
|
||||||
|
shaderInfoBuilder.AddStageInfo(shader.Info);
|
||||||
if (shader.Info.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
fragmentOutputMap = shader.Info.FragmentOutputMap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderInfo shaderInfo = compilation.SpecializationState.PipelineState.HasValue
|
ShaderInfo shaderInfo = shaderInfoBuilder.Build(compilation.SpecializationState.PipelineState, fromCache: true);
|
||||||
? new ShaderInfo(fragmentOutputMap, compilation.SpecializationState.PipelineState.Value, fromCache: true)
|
|
||||||
: new ShaderInfo(fragmentOutputMap, fromCache: true);
|
|
||||||
|
|
||||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo);
|
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo);
|
||||||
CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders);
|
CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders);
|
||||||
|
|
||||||
|
@@ -42,25 +42,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
int binaryCodeLength = reader.ReadInt32();
|
int binaryCodeLength = reader.ReadInt32();
|
||||||
byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
|
byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
|
||||||
|
|
||||||
output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv));
|
output.Add(new ShaderSource(binaryCode, stage, TargetLanguage.Spirv));
|
||||||
}
|
}
|
||||||
|
|
||||||
return output.ToArray();
|
return output.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < stages.Length; i++)
|
|
||||||
{
|
|
||||||
CachedShaderStage currentStage = stages[i];
|
|
||||||
|
|
||||||
if (currentStage?.Info != null && currentStage.Info.Stage == stage)
|
|
||||||
{
|
|
||||||
return ShaderCache.GetBindings(currentStage.Info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ShaderBindings(Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -110,16 +110,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
Logger.Error?.Print(LogClass.Gpu, $"{resourceName} index {index} exceeds per stage limit of {maxPerStage}.");
|
Logger.Error?.Print(LogClass.Gpu, $"{resourceName} index {index} exceeds per stage limit of {maxPerStage}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetStageIndex() * (int)maxPerStage + index;
|
return GetStageIndex(_stageIndex) * (int)maxPerStage + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetStageIndex()
|
public static int GetStageIndex(int stageIndex)
|
||||||
{
|
{
|
||||||
// This is just a simple remapping to ensure that most frequently used shader stages
|
// This is just a simple remapping to ensure that most frequently used shader stages
|
||||||
// have the lowest binding numbers.
|
// have the lowest binding numbers.
|
||||||
// This is useful because if we need to run on a system with a low limit on the bindings,
|
// This is useful because if we need to run on a system with a low limit on the bindings,
|
||||||
// then we can still get most games working as the most common shaders will have low binding numbers.
|
// then we can still get most games working as the most common shaders will have low binding numbers.
|
||||||
return _stageIndex switch
|
return stageIndex switch
|
||||||
{
|
{
|
||||||
4 => 1, // Fragment
|
4 => 1, // Fragment
|
||||||
3 => 2, // Geometry
|
3 => 2, // Geometry
|
||||||
|
@@ -219,12 +219,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
|
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
|
||||||
|
|
||||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
||||||
|
|
||||||
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
|
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
|
||||||
|
|
||||||
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
|
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
|
||||||
|
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
|
||||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1));
|
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
||||||
|
|
||||||
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
|
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
|
||||||
|
|
||||||
@@ -363,6 +362,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
TranslatorContext previousStage = null;
|
TranslatorContext previousStage = null;
|
||||||
|
|
||||||
|
ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context);
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||||
{
|
{
|
||||||
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
||||||
@@ -398,6 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
if (program != null)
|
if (program != null)
|
||||||
{
|
{
|
||||||
shaderSources.Add(CreateShaderSource(program));
|
shaderSources.Add(CreateShaderSource(program));
|
||||||
|
infoBuilder.AddStageInfo(program.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
previousStage = currentStage;
|
previousStage = currentStage;
|
||||||
@@ -414,8 +416,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
|
ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
|
||||||
|
|
||||||
int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
|
ShaderInfo info = infoBuilder.Build(pipeline);
|
||||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline));
|
|
||||||
|
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
||||||
|
|
||||||
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
|
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
|
||||||
|
|
||||||
@@ -466,7 +469,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <returns>Shader source</returns>
|
/// <returns>Shader source</returns>
|
||||||
public static ShaderSource CreateShaderSource(ShaderProgram program)
|
public static ShaderSource CreateShaderSource(ShaderProgram program)
|
||||||
{
|
{
|
||||||
return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language);
|
return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -717,25 +720,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets information about the bindings used by a shader program.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">Shader program information to get the information from</param>
|
|
||||||
/// <returns>Shader bindings</returns>
|
|
||||||
public static ShaderBindings GetBindings(ShaderProgramInfo info)
|
|
||||||
{
|
|
||||||
var uniformBufferBindings = info.CBuffers.Select(x => x.Binding).ToArray();
|
|
||||||
var storageBufferBindings = info.SBuffers.Select(x => x.Binding).ToArray();
|
|
||||||
var textureBindings = info.Textures.Select(x => x.Binding).ToArray();
|
|
||||||
var imageBindings = info.Images.Select(x => x.Binding).ToArray();
|
|
||||||
|
|
||||||
return new ShaderBindings(
|
|
||||||
uniformBufferBindings,
|
|
||||||
storageBufferBindings,
|
|
||||||
textureBindings,
|
|
||||||
imageBindings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates shader translation options with the requested graphics API and flags.
|
/// Creates shader translation options with the requested graphics API and flags.
|
||||||
/// The shader language is choosen based on the current configuration and graphics API.
|
/// The shader language is choosen based on the current configuration and graphics API.
|
||||||
|
260
src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
Normal file
260
src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Shader info structure builder.
|
||||||
|
/// </summary>
|
||||||
|
class ShaderInfoBuilder
|
||||||
|
{
|
||||||
|
private const int TotalSets = 4;
|
||||||
|
|
||||||
|
private const int UniformSetIndex = 0;
|
||||||
|
private const int StorageSetIndex = 1;
|
||||||
|
private const int TextureSetIndex = 2;
|
||||||
|
private const int ImageSetIndex = 3;
|
||||||
|
|
||||||
|
private const ResourceStages SupportBufferStags =
|
||||||
|
ResourceStages.Compute |
|
||||||
|
ResourceStages.Vertex |
|
||||||
|
ResourceStages.Fragment;
|
||||||
|
|
||||||
|
private readonly GpuContext _context;
|
||||||
|
|
||||||
|
private int _fragmentOutputMap;
|
||||||
|
|
||||||
|
private readonly List<ResourceDescriptor>[] _resourceDescriptors;
|
||||||
|
private readonly List<ResourceUsage>[] _resourceUsages;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new shader info builder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
|
||||||
|
public ShaderInfoBuilder(GpuContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
|
||||||
|
_fragmentOutputMap = -1;
|
||||||
|
|
||||||
|
_resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
|
||||||
|
_resourceUsages = new List<ResourceUsage>[TotalSets];
|
||||||
|
|
||||||
|
for (int index = 0; index < TotalSets; index++)
|
||||||
|
{
|
||||||
|
_resourceDescriptors[index] = new();
|
||||||
|
_resourceUsages[index] = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
AddDescriptor(SupportBufferStags, ResourceType.UniformBuffer, UniformSetIndex, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds information from a given shader stage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">Shader stage information</param>
|
||||||
|
public void AddStageInfo(ShaderProgramInfo info)
|
||||||
|
{
|
||||||
|
if (info.Stage == ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
_fragmentOutputMap = info.FragmentOutputMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stageIndex = GpuAccessorBase.GetStageIndex(info.Stage switch
|
||||||
|
{
|
||||||
|
ShaderStage.TessellationControl => 1,
|
||||||
|
ShaderStage.TessellationEvaluation => 2,
|
||||||
|
ShaderStage.Geometry => 3,
|
||||||
|
ShaderStage.Fragment => 4,
|
||||||
|
_ => 0
|
||||||
|
});
|
||||||
|
|
||||||
|
ResourceStages stages = info.Stage switch
|
||||||
|
{
|
||||||
|
ShaderStage.Compute => ResourceStages.Compute,
|
||||||
|
ShaderStage.Vertex => ResourceStages.Vertex,
|
||||||
|
ShaderStage.TessellationControl => ResourceStages.TessellationControl,
|
||||||
|
ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
|
||||||
|
ShaderStage.Geometry => ResourceStages.Geometry,
|
||||||
|
ShaderStage.Fragment => ResourceStages.Fragment,
|
||||||
|
_ => ResourceStages.None
|
||||||
|
};
|
||||||
|
|
||||||
|
int uniformsPerStage = (int)_context.Capabilities.MaximumUniformBuffersPerStage;
|
||||||
|
int storagesPerStage = (int)_context.Capabilities.MaximumStorageBuffersPerStage;
|
||||||
|
int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage;
|
||||||
|
int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage;
|
||||||
|
|
||||||
|
int uniformBinding = 1 + stageIndex * uniformsPerStage;
|
||||||
|
int storageBinding = stageIndex * storagesPerStage;
|
||||||
|
int textureBinding = stageIndex * texturesPerStage * 2;
|
||||||
|
int imageBinding = stageIndex * imagesPerStage * 2;
|
||||||
|
|
||||||
|
AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
|
||||||
|
AddArrayDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
|
||||||
|
AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
|
||||||
|
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
|
||||||
|
|
||||||
|
AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false);
|
||||||
|
AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true);
|
||||||
|
AddUsage(info.Textures, stages, TextureSetIndex, isImage: false);
|
||||||
|
AddUsage(info.Images, stages, ImageSetIndex, isImage: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a resource descriptor to the list of descriptors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stages">Shader stages where the resource is used</param>
|
||||||
|
/// <param name="type">Type of the resource</param>
|
||||||
|
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||||
|
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||||
|
/// <param name="count">Number of resources bound at the binding location</param>
|
||||||
|
private void AddDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < count; index++)
|
||||||
|
{
|
||||||
|
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding + index, 1, type, stages));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two interleaved groups of resources to the list of descriptors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stages">Shader stages where the resource is used</param>
|
||||||
|
/// <param name="type">Type of the first interleaved resource</param>
|
||||||
|
/// <param name="type2">Type of the second interleaved resource</param>
|
||||||
|
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||||
|
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||||
|
/// <param name="count">Number of resources bound at the binding location</param>
|
||||||
|
private void AddDualDescriptor(ResourceStages stages, ResourceType type, ResourceType type2, int setIndex, int binding, int count)
|
||||||
|
{
|
||||||
|
AddDescriptor(stages, type, setIndex, binding, count);
|
||||||
|
AddDescriptor(stages, type2, setIndex, binding + count, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds an array resource to the list of descriptors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stages">Shader stages where the resource is used</param>
|
||||||
|
/// <param name="type">Type of the resource</param>
|
||||||
|
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||||
|
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||||
|
/// <param name="count">Number of resources bound at the binding location</param>
|
||||||
|
private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
|
||||||
|
{
|
||||||
|
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds buffer usage information to the list of usages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffers">Buffers to be added</param>
|
||||||
|
/// <param name="stages">Stages where the buffers are used</param>
|
||||||
|
/// <param name="setIndex">Descriptor set index where the buffers will be bound</param>
|
||||||
|
/// <param name="isStorage">True for storage buffers, false for uniform buffers</param>
|
||||||
|
private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, int setIndex, bool isStorage)
|
||||||
|
{
|
||||||
|
foreach (BufferDescriptor buffer in buffers)
|
||||||
|
{
|
||||||
|
_resourceUsages[setIndex].Add(new ResourceUsage(
|
||||||
|
buffer.Binding,
|
||||||
|
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
|
||||||
|
stages,
|
||||||
|
buffer.Flags.HasFlag(BufferUsageFlags.Write) ? ResourceAccess.ReadWrite : ResourceAccess.Read));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds texture usage information to the list of usages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="textures">Textures to be added</param>
|
||||||
|
/// <param name="stages">Stages where the textures are used</param>
|
||||||
|
/// <param name="setIndex">Descriptor set index where the textures will be bound</param>
|
||||||
|
/// <param name="isImage">True for images, false for textures</param>
|
||||||
|
private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage)
|
||||||
|
{
|
||||||
|
foreach (TextureDescriptor texture in textures)
|
||||||
|
{
|
||||||
|
bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
|
||||||
|
|
||||||
|
ResourceType type = isBuffer
|
||||||
|
? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
|
||||||
|
: (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
|
||||||
|
|
||||||
|
_resourceUsages[setIndex].Add(new ResourceUsage(
|
||||||
|
texture.Binding,
|
||||||
|
type,
|
||||||
|
stages,
|
||||||
|
texture.Flags.HasFlag(TextureUsageFlags.ImageStore) ? ResourceAccess.ReadWrite : ResourceAccess.Read));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new shader information structure from the added information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pipeline">Optional pipeline state for background shader compilation</param>
|
||||||
|
/// <param name="fromCache">Indicates if the shader comes from a disk cache</param>
|
||||||
|
/// <returns>Shader information</returns>
|
||||||
|
public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false)
|
||||||
|
{
|
||||||
|
var descriptors = new ResourceDescriptorCollection[TotalSets];
|
||||||
|
var usages = new ResourceUsageCollection[TotalSets];
|
||||||
|
|
||||||
|
for (int index = 0; index < TotalSets; index++)
|
||||||
|
{
|
||||||
|
descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
|
||||||
|
usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceLayout resourceLayout = new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
|
||||||
|
|
||||||
|
if (pipeline.HasValue)
|
||||||
|
{
|
||||||
|
return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds shader information for shaders from the disk cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that owns the shaders</param>
|
||||||
|
/// <param name="programs">Shaders from the disk cache</param>
|
||||||
|
/// <param name="pipeline">Optional pipeline for background compilation</param>
|
||||||
|
/// <returns>Shader information</returns>
|
||||||
|
public static ShaderInfo BuildForCache(GpuContext context, IEnumerable<CachedShaderStage> programs, ProgramPipelineState? pipeline)
|
||||||
|
{
|
||||||
|
ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
|
||||||
|
|
||||||
|
foreach (CachedShaderStage program in programs)
|
||||||
|
{
|
||||||
|
if (program?.Info != null)
|
||||||
|
{
|
||||||
|
builder.AddStageInfo(program.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Build(pipeline, fromCache: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds shader information for a compute shader.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that owns the shader</param>
|
||||||
|
/// <param name="info">Compute shader information</param>
|
||||||
|
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
|
||||||
|
/// <returns>Shader information</returns>
|
||||||
|
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
|
||||||
|
{
|
||||||
|
ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
|
||||||
|
|
||||||
|
builder.AddStageInfo(info);
|
||||||
|
|
||||||
|
return builder.Build(null, fromCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -239,33 +239,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFragment = context.Config.Stage == ShaderStage.Fragment;
|
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryEarlyZForce())
|
||||||
|
|
||||||
if (isFragment || context.Config.Stage == ShaderStage.Compute || context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
{
|
||||||
if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce())
|
context.AppendLine("layout(early_fragment_tests) in;");
|
||||||
{
|
context.AppendLine();
|
||||||
context.AppendLine("layout(early_fragment_tests) in;");
|
|
||||||
context.AppendLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0)
|
|
||||||
{
|
|
||||||
string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage);
|
|
||||||
|
|
||||||
int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length;
|
|
||||||
|
|
||||||
if (isFragment)
|
|
||||||
{
|
|
||||||
scaleElements++; // Also includes render target scale, for gl_FragCoord.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0)
|
|
||||||
{
|
|
||||||
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
|
|
||||||
context.AppendLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
|
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
if (scale < 0.0) // If less than 0, try interpolate between texels by using the screen position.
|
|
||||||
{
|
|
||||||
return ivec2(vec2(inputVec) * (-scale) + mod(gl_FragCoord.xy, 0.0 - scale));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@@ -1,20 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@@ -101,6 +101,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.MemoryBarrier, InstType.CallNullary, "memoryBarrier");
|
Add(Instruction.MemoryBarrier, InstType.CallNullary, "memoryBarrier");
|
||||||
Add(Instruction.Minimum, InstType.CallBinary, "min");
|
Add(Instruction.Minimum, InstType.CallBinary, "min");
|
||||||
Add(Instruction.MinimumU32, InstType.CallBinary, "min");
|
Add(Instruction.MinimumU32, InstType.CallBinary, "min");
|
||||||
|
Add(Instruction.Modulo, InstType.CallBinary, "mod");
|
||||||
Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1);
|
Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1);
|
||||||
Add(Instruction.MultiplyHighS32, InstType.CallBinary, HelperFunctionNames.MultiplyHighS32);
|
Add(Instruction.MultiplyHighS32, InstType.CallBinary, HelperFunctionNames.MultiplyHighS32);
|
||||||
Add(Instruction.MultiplyHighU32, InstType.CallBinary, HelperFunctionNames.MultiplyHighU32);
|
Add(Instruction.MultiplyHighU32, InstType.CallBinary, HelperFunctionNames.MultiplyHighU32);
|
||||||
|
@@ -97,30 +97,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
texCallBuilder.Append(str);
|
texCallBuilder.Append(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
string ApplyScaling(string vector)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
texOp.Inst == Instruction.ImageLoad &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
// Image scales start after texture ones.
|
|
||||||
int scaleIndex = context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
if (pCount == 3 && isArray)
|
|
||||||
{
|
|
||||||
// The array index is not scaled, just x and y.
|
|
||||||
vector = $"ivec3(Helper_TexelFetchScale(({vector}).xy, {scaleIndex}), ({vector}).z)";
|
|
||||||
}
|
|
||||||
else if (pCount == 2 && !isArray)
|
|
||||||
{
|
|
||||||
vector = $"Helper_TexelFetchScale({vector}, {scaleIndex})";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pCount > 1)
|
if (pCount > 1)
|
||||||
{
|
{
|
||||||
string[] elems = new string[pCount];
|
string[] elems = new string[pCount];
|
||||||
@@ -130,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
elems[index] = Src(AggregateType.S32);
|
elems[index] = Src(AggregateType.S32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Append(ApplyScaling($"ivec{pCount}({string.Join(", ", elems)})"));
|
Append($"ivec{pCount}({string.Join(", ", elems)})");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -584,53 +560,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string ApplyScaling(string vector)
|
Append(AssemblePVector(pCount));
|
||||||
{
|
|
||||||
if (intCoords)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
int index = context.Config.FindTextureDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
if (pCount == 3 && isArray)
|
|
||||||
{
|
|
||||||
// The array index is not scaled, just x and y.
|
|
||||||
vector = "ivec3(Helper_TexelFetchScale((" + vector + ").xy, " + index + "), (" + vector + ").z)";
|
|
||||||
}
|
|
||||||
else if (pCount == 2 && !isArray)
|
|
||||||
{
|
|
||||||
vector = "Helper_TexelFetchScale(" + vector + ", " + index + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
string ApplyBias(string vector)
|
|
||||||
{
|
|
||||||
int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision();
|
|
||||||
if (isGather && gatherBiasPrecision != 0)
|
|
||||||
{
|
|
||||||
// GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels.
|
|
||||||
// Offset by the gather precision divided by 2 to correct for rounding.
|
|
||||||
|
|
||||||
if (pCount == 1)
|
|
||||||
{
|
|
||||||
vector = $"{vector} + (1.0 / (float(textureSize({samplerName}, 0)) * float({1 << (gatherBiasPrecision + 1)})))";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vector = $"{vector} + (1.0 / (vec{pCount}(textureSize({samplerName}, 0).{"xyz".Substring(0, pCount)}) * float({1 << (gatherBiasPrecision + 1)})))";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
Append(ApplyBias(ApplyScaling(AssemblePVector(pCount))));
|
|
||||||
|
|
||||||
string AssembleDerivativesVector(int count)
|
string AssembleDerivativesVector(int count)
|
||||||
{
|
{
|
||||||
@@ -750,7 +680,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(TextureDescriptor descriptor, int descriptorIndex) = context.Config.FindTextureDescriptor(texOp);
|
TextureDescriptor descriptor = context.Config.FindTextureDescriptor(texOp);
|
||||||
bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer;
|
bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer;
|
||||||
string texCall;
|
string texCall;
|
||||||
|
|
||||||
@@ -767,14 +697,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
texCall = $"textureSize({samplerName}){GetMask(texOp.Index)}";
|
texCall = $"textureSize({samplerName}){GetMask(texOp.Index)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
(texOp.Index < 2 || (texOp.Type & SamplerType.Mask) == SamplerType.Texture3D) &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
texCall = $"Helper_TextureSizeUnscale({texCall}, {descriptorIndex})";
|
|
||||||
}
|
|
||||||
|
|
||||||
return texCall;
|
return texCall;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
|
public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||||
|
|
||||||
public Instruction CoordTemp { get; set; }
|
public Instruction CoordTemp { get; set; }
|
||||||
|
public StructuredFunction CurrentFunction { get; set; }
|
||||||
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
|
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
|
||||||
private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
|
private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
|
||||||
private readonly Dictionary<int, Instruction> _funcArgs = new Dictionary<int, Instruction>();
|
private readonly Dictionary<int, Instruction> _funcArgs = new Dictionary<int, Instruction>();
|
||||||
|
@@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader.Translation;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
|
||||||
@@ -114,6 +113,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Add(Instruction.MemoryBarrier, GenerateMemoryBarrier);
|
Add(Instruction.MemoryBarrier, GenerateMemoryBarrier);
|
||||||
Add(Instruction.Minimum, GenerateMinimum);
|
Add(Instruction.Minimum, GenerateMinimum);
|
||||||
Add(Instruction.MinimumU32, GenerateMinimumU32);
|
Add(Instruction.MinimumU32, GenerateMinimumU32);
|
||||||
|
Add(Instruction.Modulo, GenerateModulo);
|
||||||
Add(Instruction.Multiply, GenerateMultiply);
|
Add(Instruction.Multiply, GenerateMultiply);
|
||||||
Add(Instruction.MultiplyHighS32, GenerateMultiplyHighS32);
|
Add(Instruction.MultiplyHighS32, GenerateMultiplyHighS32);
|
||||||
Add(Instruction.MultiplyHighU32, GenerateMultiplyHighU32);
|
Add(Instruction.MultiplyHighU32, GenerateMultiplyHighU32);
|
||||||
@@ -744,8 +744,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
pCoords = Src(AggregateType.S32);
|
pCoords = Src(AggregateType.S32);
|
||||||
}
|
}
|
||||||
|
|
||||||
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords: true, isBindless, isIndexed, isArray, pCount);
|
|
||||||
|
|
||||||
(var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
|
(var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
|
||||||
|
|
||||||
var image = context.Load(imageType, imageVariable);
|
var image = context.Load(imageType, imageVariable);
|
||||||
@@ -1040,6 +1038,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return GenerateBinaryU32(context, operation, context.Delegates.GlslUMin);
|
return GenerateBinaryU32(context, operation, context.Delegates.GlslUMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static OperationResult GenerateModulo(CodeGenContext context, AstOperation operation)
|
||||||
|
{
|
||||||
|
return GenerateBinary(context, operation, context.Delegates.FMod, null);
|
||||||
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateMultiply(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateMultiply(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
return GenerateBinary(context, operation, context.Delegates.FMul, context.Delegates.IMul);
|
return GenerateBinary(context, operation, context.Delegates.FMul, context.Delegates.IMul);
|
||||||
@@ -1101,7 +1104,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
context.Return();
|
if (operation.SourcesCount != 0)
|
||||||
|
{
|
||||||
|
context.ReturnValue(context.Get(context.CurrentFunction.ReturnType, operation.GetSource(0)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Return();
|
||||||
|
}
|
||||||
|
|
||||||
return OperationResult.Invalid;
|
return OperationResult.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1439,35 +1450,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SpvInstruction ApplyBias(SpvInstruction vector, SpvInstruction image)
|
|
||||||
{
|
|
||||||
int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision();
|
|
||||||
if (isGather && gatherBiasPrecision != 0)
|
|
||||||
{
|
|
||||||
// GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels.
|
|
||||||
// Offset by the gather precision divided by 2 to correct for rounding.
|
|
||||||
var sizeType = pCount == 1 ? context.TypeS32() : context.TypeVector(context.TypeS32(), pCount);
|
|
||||||
var pVectorType = pCount == 1 ? context.TypeFP32() : context.TypeVector(context.TypeFP32(), pCount);
|
|
||||||
|
|
||||||
var bias = context.Constant(context.TypeFP32(), (float)(1 << (gatherBiasPrecision + 1)));
|
|
||||||
var biasVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(bias, pCount).ToArray());
|
|
||||||
|
|
||||||
var one = context.Constant(context.TypeFP32(), 1f);
|
|
||||||
var oneVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(one, pCount).ToArray());
|
|
||||||
|
|
||||||
var divisor = context.FMul(
|
|
||||||
pVectorType,
|
|
||||||
context.ConvertSToF(pVectorType, context.ImageQuerySize(sizeType, image)),
|
|
||||||
biasVector);
|
|
||||||
|
|
||||||
vector = context.FAdd(pVectorType, vector, context.FDiv(pVectorType, oneVector, divisor));
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpvInstruction pCoords = AssemblePVector(pCount);
|
SpvInstruction pCoords = AssemblePVector(pCount);
|
||||||
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount);
|
|
||||||
|
|
||||||
SpvInstruction AssembleDerivativesVector(int count)
|
SpvInstruction AssembleDerivativesVector(int count)
|
||||||
{
|
{
|
||||||
@@ -1638,8 +1621,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
image = context.Image(imageType, image);
|
image = context.Image(imageType, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
pCoords = ApplyBias(pCoords, image);
|
|
||||||
|
|
||||||
var operands = operandsList.ToArray();
|
var operands = operandsList.ToArray();
|
||||||
|
|
||||||
SpvInstruction result;
|
SpvInstruction result;
|
||||||
@@ -1755,11 +1736,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
result = context.CompositeExtract(context.TypeS32(), result, (SpvLiteralInteger)texOp.Index);
|
result = context.CompositeExtract(context.TypeS32(), result, (SpvLiteralInteger)texOp.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texOp.Index < 2 || (type & SamplerType.Mask) == SamplerType.Texture3D)
|
|
||||||
{
|
|
||||||
result = ScalingHelpers.ApplyUnscaling(context, texOp.WithType(type), result, isBindless, isIndexed);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OperationResult(AggregateType.S32, result);
|
return new OperationResult(AggregateType.S32, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,227 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
|
||||||
using static Spv.Specification;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|
||||||
{
|
|
||||||
using SpvInstruction = Spv.Generator.Instruction;
|
|
||||||
|
|
||||||
static class ScalingHelpers
|
|
||||||
{
|
|
||||||
public static SpvInstruction ApplyScaling(
|
|
||||||
CodeGenContext context,
|
|
||||||
AstTextureOperation texOp,
|
|
||||||
SpvInstruction vector,
|
|
||||||
bool intCoords,
|
|
||||||
bool isBindless,
|
|
||||||
bool isIndexed,
|
|
||||||
bool isArray,
|
|
||||||
int pCount)
|
|
||||||
{
|
|
||||||
if (intCoords)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
int index = texOp.Inst == Instruction.ImageLoad
|
|
||||||
? context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp)
|
|
||||||
: context.Config.FindTextureDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
if (pCount == 3 && isArray)
|
|
||||||
{
|
|
||||||
return ApplyScaling2DArray(context, vector, index);
|
|
||||||
}
|
|
||||||
else if (pCount == 2 && !isArray)
|
|
||||||
{
|
|
||||||
return ApplyScaling2D(context, vector, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SpvInstruction ApplyScaling2DArray(CodeGenContext context, SpvInstruction vector, int index)
|
|
||||||
{
|
|
||||||
// The array index is not scaled, just x and y.
|
|
||||||
var vectorXY = context.VectorShuffle(context.TypeVector(context.TypeS32(), 2), vector, vector, 0, 1);
|
|
||||||
var vectorZ = context.CompositeExtract(context.TypeS32(), vector, 2);
|
|
||||||
var vectorXYScaled = ApplyScaling2D(context, vectorXY, index);
|
|
||||||
var vectorScaled = context.CompositeConstruct(context.TypeVector(context.TypeS32(), 3), vectorXYScaled, vectorZ);
|
|
||||||
|
|
||||||
return vectorScaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SpvInstruction ApplyScaling2D(CodeGenContext context, SpvInstruction vector, int index)
|
|
||||||
{
|
|
||||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
|
||||||
var fieldIndex = context.Constant(context.TypeU32(), 4);
|
|
||||||
var scaleIndex = context.Constant(context.TypeU32(), index);
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
|
||||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3));
|
|
||||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
|
||||||
|
|
||||||
var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex);
|
|
||||||
var scale = context.Load(context.TypeFP32(), scaleElemPointer);
|
|
||||||
|
|
||||||
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
|
|
||||||
var localVector = context.CoordTemp;
|
|
||||||
|
|
||||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
|
||||||
|
|
||||||
var mergeLabel = context.Label();
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
var scaledInterpolatedLabel = context.Label();
|
|
||||||
var scaledNoInterpolationLabel = context.Label();
|
|
||||||
|
|
||||||
var needsInterpolation = context.FOrdLessThan(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 0f));
|
|
||||||
|
|
||||||
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(needsInterpolation, scaledInterpolatedLabel, scaledNoInterpolationLabel);
|
|
||||||
|
|
||||||
// scale < 0.0
|
|
||||||
context.AddLabel(scaledInterpolatedLabel);
|
|
||||||
|
|
||||||
ApplyScalingInterpolated(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
// scale >= 0.0
|
|
||||||
context.AddLabel(scaledNoInterpolationLabel);
|
|
||||||
|
|
||||||
ApplyScalingNoInterpolation(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(mergeLabel);
|
|
||||||
|
|
||||||
var passthroughLabel = context.Label();
|
|
||||||
var finalMergeLabel = context.Label();
|
|
||||||
|
|
||||||
context.SelectionMerge(finalMergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(passthrough, passthroughLabel, finalMergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(passthroughLabel);
|
|
||||||
|
|
||||||
context.Store(localVector, vector);
|
|
||||||
context.Branch(finalMergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(finalMergeLabel);
|
|
||||||
|
|
||||||
return context.Load(ivector2Type, localVector);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var passthroughLabel = context.Label();
|
|
||||||
var scaledLabel = context.Label();
|
|
||||||
|
|
||||||
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(passthrough, passthroughLabel, scaledLabel);
|
|
||||||
|
|
||||||
// scale == 1.0
|
|
||||||
context.AddLabel(passthroughLabel);
|
|
||||||
|
|
||||||
context.Store(localVector, vector);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
// scale != 1.0
|
|
||||||
context.AddLabel(scaledLabel);
|
|
||||||
|
|
||||||
ApplyScalingNoInterpolation(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(mergeLabel);
|
|
||||||
|
|
||||||
return context.Load(ivector2Type, localVector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyScalingInterpolated(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
|
|
||||||
{
|
|
||||||
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
|
|
||||||
|
|
||||||
var scaleNegated = context.FNegate(context.TypeFP32(), scale);
|
|
||||||
var scaleVector = context.CompositeConstruct(vector2Type, scaleNegated, scaleNegated);
|
|
||||||
|
|
||||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
|
||||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated);
|
|
||||||
|
|
||||||
var fragCoordPointer = context.Inputs[new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)];
|
|
||||||
var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer);
|
|
||||||
var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1);
|
|
||||||
|
|
||||||
var scaleMod = context.FMod(vector2Type, fragCoordXY, scaleVector);
|
|
||||||
var vectorInterpolated = context.FAdd(vector2Type, vectorScaled, scaleMod);
|
|
||||||
|
|
||||||
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorInterpolated));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyScalingNoInterpolation(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
scale = context.GlslFAbs(context.TypeFP32(), scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
|
|
||||||
|
|
||||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
|
||||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scale);
|
|
||||||
|
|
||||||
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorScaled));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SpvInstruction ApplyUnscaling(
|
|
||||||
CodeGenContext context,
|
|
||||||
AstTextureOperation texOp,
|
|
||||||
SpvInstruction size,
|
|
||||||
bool isBindless,
|
|
||||||
bool isIndexed)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
int index = context.Config.FindTextureDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
|
||||||
var fieldIndex = context.Constant(context.TypeU32(), 4);
|
|
||||||
var scaleIndex = context.Constant(context.TypeU32(), index);
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
|
||||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3));
|
|
||||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
|
||||||
|
|
||||||
var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex);
|
|
||||||
var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer));
|
|
||||||
|
|
||||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
|
||||||
|
|
||||||
var sizeFloat = context.ConvertSToF(context.TypeFP32(), size);
|
|
||||||
var sizeUnscaled = context.FDiv(context.TypeFP32(), sizeFloat, scale);
|
|
||||||
var sizeUnscaledInt = context.ConvertFToS(context.TypeS32(), sizeUnscaled);
|
|
||||||
|
|
||||||
return context.Select(context.TypeS32(), passthrough, size, sizeUnscaledInt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
public readonly FuncBinaryInstruction GlslSMax;
|
public readonly FuncBinaryInstruction GlslSMax;
|
||||||
public readonly FuncBinaryInstruction GlslFMin;
|
public readonly FuncBinaryInstruction GlslFMin;
|
||||||
public readonly FuncBinaryInstruction GlslSMin;
|
public readonly FuncBinaryInstruction GlslSMin;
|
||||||
|
public readonly FuncBinaryInstruction FMod;
|
||||||
public readonly FuncBinaryInstruction FMul;
|
public readonly FuncBinaryInstruction FMul;
|
||||||
public readonly FuncBinaryInstruction IMul;
|
public readonly FuncBinaryInstruction IMul;
|
||||||
public readonly FuncBinaryInstruction FSub;
|
public readonly FuncBinaryInstruction FSub;
|
||||||
@@ -174,6 +175,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
GlslSMax = context.GlslSMax;
|
GlslSMax = context.GlslSMax;
|
||||||
GlslFMin = context.GlslFMin;
|
GlslFMin = context.GlslFMin;
|
||||||
GlslSMin = context.GlslSMin;
|
GlslSMin = context.GlslSMin;
|
||||||
|
FMod = context.FMod;
|
||||||
FMul = context.FMul;
|
FMul = context.FMul;
|
||||||
IMul = context.IMul;
|
IMul = context.IMul;
|
||||||
FSub = context.FSub;
|
FSub = context.FSub;
|
||||||
|
@@ -144,10 +144,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex)
|
private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex)
|
||||||
{
|
{
|
||||||
var function = info.Functions[funcIndex];
|
(var function, var spvFunc) = context.GetFunction(funcIndex);
|
||||||
|
|
||||||
(_, var spvFunc) = context.GetFunction(funcIndex);
|
|
||||||
|
|
||||||
|
context.CurrentFunction = function;
|
||||||
context.AddFunction(spvFunc);
|
context.AddFunction(spvFunc);
|
||||||
context.StartFunction();
|
context.StartFunction();
|
||||||
|
|
||||||
|
@@ -187,27 +187,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented.");
|
context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void P2rR(EmitterContext context)
|
|
||||||
{
|
|
||||||
InstP2rR op = context.GetOp<InstP2rR>();
|
|
||||||
|
|
||||||
context.Config.GpuAccessor.Log("Shader instruction P2rR is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void P2rI(EmitterContext context)
|
|
||||||
{
|
|
||||||
InstP2rI op = context.GetOp<InstP2rI>();
|
|
||||||
|
|
||||||
context.Config.GpuAccessor.Log("Shader instruction P2rI is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void P2rC(EmitterContext context)
|
|
||||||
{
|
|
||||||
InstP2rC op = context.GetOp<InstP2rC>();
|
|
||||||
|
|
||||||
context.Config.GpuAccessor.Log("Shader instruction P2rC is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Pexit(EmitterContext context)
|
public static void Pexit(EmitterContext context)
|
||||||
{
|
{
|
||||||
InstPexit op = context.GetOp<InstPexit>();
|
InstPexit op = context.GetOp<InstPexit>();
|
||||||
|
@@ -209,21 +209,15 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
return context.ICompareNotEqual(context.BitwiseAnd(value, Const(1 << bit)), Const(0));
|
return context.ICompareNotEqual(context.BitwiseAnd(value, Const(1 << bit)), Const(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ccpr)
|
int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount;
|
||||||
{
|
RegisterType type = ccpr ? RegisterType.Flag : RegisterType.Predicate;
|
||||||
// TODO: Support Register to condition code flags copy.
|
int shift = (int)byteSel * 8;
|
||||||
context.Config.GpuAccessor.Log("R2P.CC not implemented.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int shift = (int)byteSel * 8;
|
|
||||||
|
|
||||||
for (int bit = 0; bit < RegisterConsts.PredsCount; bit++)
|
for (int bit = 0; bit < count; bit++)
|
||||||
{
|
{
|
||||||
Operand pred = Register(bit, RegisterType.Predicate);
|
Operand flag = Register(bit, type);
|
||||||
Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), pred);
|
Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), flag);
|
||||||
context.Copy(pred, res);
|
context.Copy(flag, res);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Graphics.Shader.Decoders;
|
using Ryujinx.Graphics.Shader.Decoders;
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
|
using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
|
||||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
@@ -50,5 +49,68 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
|
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
|
||||||
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
|
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void P2rC(EmitterContext context)
|
||||||
|
{
|
||||||
|
InstP2rC op = context.GetOp<InstP2rC>();
|
||||||
|
|
||||||
|
Operand srcA = GetSrcReg(context, op.SrcA);
|
||||||
|
Operand dest = GetSrcReg(context, op.Dest);
|
||||||
|
Operand mask = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
|
||||||
|
|
||||||
|
EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void P2rI(EmitterContext context)
|
||||||
|
{
|
||||||
|
InstP2rI op = context.GetOp<InstP2rI>();
|
||||||
|
|
||||||
|
Operand srcA = GetSrcReg(context, op.SrcA);
|
||||||
|
Operand dest = GetSrcReg(context, op.Dest);
|
||||||
|
Operand mask = GetSrcImm(context, op.Imm20);
|
||||||
|
|
||||||
|
EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void P2rR(EmitterContext context)
|
||||||
|
{
|
||||||
|
InstP2rR op = context.GetOp<InstP2rR>();
|
||||||
|
|
||||||
|
Operand srcA = GetSrcReg(context, op.SrcA);
|
||||||
|
Operand dest = GetSrcReg(context, op.Dest);
|
||||||
|
Operand mask = GetSrcReg(context, op.SrcB);
|
||||||
|
|
||||||
|
EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitP2r(
|
||||||
|
EmitterContext context,
|
||||||
|
Operand srcA,
|
||||||
|
Operand dest,
|
||||||
|
Operand mask,
|
||||||
|
ByteSel byteSel,
|
||||||
|
bool ccpr)
|
||||||
|
{
|
||||||
|
int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount;
|
||||||
|
int shift = (int)byteSel * 8;
|
||||||
|
mask = context.BitwiseAnd(mask, Const(0xff));
|
||||||
|
|
||||||
|
Operand insert = Const(0);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
Operand condition = ccpr
|
||||||
|
? Register(i, RegisterType.Flag)
|
||||||
|
: Register(i, RegisterType.Predicate);
|
||||||
|
|
||||||
|
Operand bit = context.ConditionalSelect(condition, Const(1 << (i + shift)), Const(0));
|
||||||
|
insert = context.BitwiseOr(insert, bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand maskShifted = context.ShiftLeft(mask, Const(shift));
|
||||||
|
Operand masked = context.BitwiseAnd(srcA, context.BitwiseNot(maskShifted));
|
||||||
|
Operand res = context.BitwiseOr(masked, context.BitwiseAnd(insert, maskShifted));
|
||||||
|
|
||||||
|
context.Copy(dest, res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -97,6 +97,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
MemoryBarrier,
|
MemoryBarrier,
|
||||||
Minimum,
|
Minimum,
|
||||||
MinimumU32,
|
MinimumU32,
|
||||||
|
Modulo,
|
||||||
Multiply,
|
Multiply,
|
||||||
MultiplyHighS32,
|
MultiplyHighS32,
|
||||||
MultiplyHighU32,
|
MultiplyHighU32,
|
||||||
|
@@ -4,10 +4,6 @@
|
|||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Spv.Generator\Spv.Generator.csproj" />
|
<ProjectReference Include="..\Spv.Generator\Spv.Generator.csproj" />
|
||||||
@@ -25,9 +21,6 @@
|
|||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_fp.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_cp.glsl" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -27,10 +27,5 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
CbufSlot = cbufSlot;
|
CbufSlot = cbufSlot;
|
||||||
Handle = handle;
|
Handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AstTextureOperation WithType(SamplerType type)
|
|
||||||
{
|
|
||||||
return new AstTextureOperation(Inst, type, Format, Flags, CbufSlot, Handle, Index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -104,6 +104,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Add(Instruction.MaximumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
Add(Instruction.MaximumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
||||||
Add(Instruction.Minimum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Minimum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.MinimumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
Add(Instruction.MinimumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
||||||
|
Add(Instruction.Modulo, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.Multiply, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Multiply, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.MultiplyHighS32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.MultiplyHighS32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.MultiplyHighU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
Add(Instruction.MultiplyHighU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
||||||
|
@@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
{
|
{
|
||||||
static class StructuredProgram
|
static class StructuredProgram
|
||||||
{
|
{
|
||||||
public static StructuredProgramInfo MakeStructuredProgram(Function[] functions, ShaderConfig config)
|
public static StructuredProgramInfo MakeStructuredProgram(IReadOnlyList<Function> functions, ShaderConfig config)
|
||||||
{
|
{
|
||||||
StructuredProgramContext context = new StructuredProgramContext(config);
|
StructuredProgramContext context = new StructuredProgramContext(config);
|
||||||
|
|
||||||
for (int funcIndex = 0; funcIndex < functions.Length; funcIndex++)
|
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
||||||
{
|
{
|
||||||
Function function = functions[funcIndex];
|
Function function = functions[funcIndex];
|
||||||
|
|
||||||
|
@@ -49,13 +49,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
private readonly List<Operation> _operations;
|
private readonly List<Operation> _operations;
|
||||||
private readonly Dictionary<ulong, BlockLabel> _labels;
|
private readonly Dictionary<ulong, BlockLabel> _labels;
|
||||||
|
|
||||||
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
|
public EmitterContext()
|
||||||
|
{
|
||||||
|
_operations = new List<Operation>();
|
||||||
|
_labels = new Dictionary<ulong, BlockLabel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) : this()
|
||||||
{
|
{
|
||||||
Program = program;
|
Program = program;
|
||||||
Config = config;
|
Config = config;
|
||||||
IsNonMain = isNonMain;
|
IsNonMain = isNonMain;
|
||||||
_operations = new List<Operation>();
|
|
||||||
_labels = new Dictionary<ulong, BlockLabel>();
|
|
||||||
|
|
||||||
EmitStart();
|
EmitStart();
|
||||||
}
|
}
|
||||||
|
@@ -307,6 +307,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return context.Add(fpType | Instruction.Minimum, Local(), a, b);
|
return context.Add(fpType | Instruction.Minimum, Local(), a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Operand FPModulo(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32)
|
||||||
|
{
|
||||||
|
return context.Add(fpType | Instruction.Modulo, Local(), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32)
|
public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32)
|
||||||
{
|
{
|
||||||
return context.Add(fpType | Instruction.Multiply, Local(), a, b);
|
return context.Add(fpType | Instruction.Multiply, Local(), a, b);
|
||||||
@@ -656,7 +661,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public static void Return(this EmitterContext context, Operand returnValue)
|
public static void Return(this EmitterContext context, Operand returnValue)
|
||||||
{
|
{
|
||||||
context.PrepareForReturn();
|
|
||||||
context.Add(Instruction.Return, null, returnValue);
|
context.Add(Instruction.Return, null, returnValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
134
src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs
Normal file
134
src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
{
|
||||||
|
class HelperFunctionManager
|
||||||
|
{
|
||||||
|
private readonly List<Function> _functionList;
|
||||||
|
private readonly Dictionary<HelperFunctionName, int> _functionIds;
|
||||||
|
private readonly ShaderStage _stage;
|
||||||
|
|
||||||
|
public HelperFunctionManager(List<Function> functionList, ShaderStage stage)
|
||||||
|
{
|
||||||
|
_functionList = functionList;
|
||||||
|
_functionIds = new Dictionary<HelperFunctionName, int>();
|
||||||
|
_stage = stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetOrCreateFunctionId(HelperFunctionName functionName)
|
||||||
|
{
|
||||||
|
if (_functionIds.TryGetValue(functionName, out int functionId))
|
||||||
|
{
|
||||||
|
return functionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Function function = GenerateFunction(functionName);
|
||||||
|
functionId = _functionList.Count;
|
||||||
|
_functionList.Add(function);
|
||||||
|
_functionIds.Add(functionName, functionId);
|
||||||
|
|
||||||
|
return functionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function GenerateFunction(HelperFunctionName functionName)
|
||||||
|
{
|
||||||
|
return functionName switch
|
||||||
|
{
|
||||||
|
HelperFunctionName.TexelFetchScale => GenerateTexelFetchScaleFunction(),
|
||||||
|
HelperFunctionName.TextureSizeUnscale => GenerateTextureSizeUnscaleFunction(),
|
||||||
|
_ => throw new ArgumentException($"Invalid function name {functionName}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function GenerateTexelFetchScaleFunction()
|
||||||
|
{
|
||||||
|
EmitterContext context = new EmitterContext();
|
||||||
|
|
||||||
|
Operand input = Argument(0);
|
||||||
|
Operand samplerIndex = Argument(1);
|
||||||
|
Operand index = GetScaleIndex(context, samplerIndex);
|
||||||
|
|
||||||
|
Operand scale = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index);
|
||||||
|
|
||||||
|
Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f));
|
||||||
|
Operand lblScaleNotOne = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(lblScaleNotOne, scaleIsOne);
|
||||||
|
context.Return(input);
|
||||||
|
context.MarkLabel(lblScaleNotOne);
|
||||||
|
|
||||||
|
int inArgumentsCount;
|
||||||
|
|
||||||
|
if (_stage == ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
Operand scaleIsLessThanZero = context.FPCompareLess(scale, ConstF(0f));
|
||||||
|
Operand lblScaleGreaterOrEqualZero = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(lblScaleGreaterOrEqualZero, scaleIsLessThanZero);
|
||||||
|
|
||||||
|
Operand negScale = context.FPNegate(scale);
|
||||||
|
Operand inputScaled = context.FPMultiply(context.IConvertS32ToFP32(input), negScale);
|
||||||
|
Operand fragCoordX = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0));
|
||||||
|
Operand fragCoordY = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1));
|
||||||
|
Operand fragCoord = context.ConditionalSelect(Argument(2), fragCoordY, fragCoordX);
|
||||||
|
Operand inputBias = context.FPModulo(fragCoord, negScale);
|
||||||
|
Operand inputWithBias = context.FPAdd(inputScaled, inputBias);
|
||||||
|
|
||||||
|
context.Return(context.FP32ConvertToS32(inputWithBias));
|
||||||
|
context.MarkLabel(lblScaleGreaterOrEqualZero);
|
||||||
|
|
||||||
|
inArgumentsCount = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inArgumentsCount = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand inputScaled2 = context.FPMultiply(context.IConvertS32ToFP32(input), scale);
|
||||||
|
|
||||||
|
context.Return(context.FP32ConvertToS32(inputScaled2));
|
||||||
|
|
||||||
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TexelFetchScale", true, inArgumentsCount, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function GenerateTextureSizeUnscaleFunction()
|
||||||
|
{
|
||||||
|
EmitterContext context = new EmitterContext();
|
||||||
|
|
||||||
|
Operand input = Argument(0);
|
||||||
|
Operand samplerIndex = Argument(1);
|
||||||
|
Operand index = GetScaleIndex(context, samplerIndex);
|
||||||
|
|
||||||
|
Operand scale = context.FPAbsolute(context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index));
|
||||||
|
|
||||||
|
Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f));
|
||||||
|
Operand lblScaleNotOne = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(lblScaleNotOne, scaleIsOne);
|
||||||
|
context.Return(input);
|
||||||
|
context.MarkLabel(lblScaleNotOne);
|
||||||
|
|
||||||
|
Operand inputUnscaled = context.FPDivide(context.IConvertS32ToFP32(input), scale);
|
||||||
|
|
||||||
|
context.Return(context.FP32ConvertToS32(inputUnscaled));
|
||||||
|
|
||||||
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TextureSizeUnscale", true, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Operand GetScaleIndex(EmitterContext context, Operand index)
|
||||||
|
{
|
||||||
|
switch (_stage)
|
||||||
|
{
|
||||||
|
case ShaderStage.Vertex:
|
||||||
|
Operand fragScaleCount = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.FragmentRenderScaleCount));
|
||||||
|
return context.IAdd(Const(1), context.IAdd(index, fragScaleCount));
|
||||||
|
default:
|
||||||
|
return context.IAdd(Const(1), index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
{
|
||||||
|
enum HelperFunctionName
|
||||||
|
{
|
||||||
|
TexelFetchScale,
|
||||||
|
TextureSizeUnscale
|
||||||
|
}
|
||||||
|
}
|
@@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
static class Rewriter
|
static class Rewriter
|
||||||
{
|
{
|
||||||
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
|
public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config)
|
||||||
{
|
{
|
||||||
bool isVertexShader = config.Stage == ShaderStage.Vertex;
|
bool isVertexShader = config.Stage == ShaderStage.Vertex;
|
||||||
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
|
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
|
||||||
@@ -54,9 +54,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
if (operation is TextureOperation texOp)
|
if (operation is TextureOperation texOp)
|
||||||
{
|
{
|
||||||
|
node = InsertTexelFetchScale(hfm, node, config);
|
||||||
|
node = InsertTextureSizeUnscale(hfm, node, config);
|
||||||
|
|
||||||
if (texOp.Inst == Instruction.TextureSample)
|
if (texOp.Inst == Instruction.TextureSample)
|
||||||
{
|
{
|
||||||
node = RewriteTextureSample(node, config);
|
node = InsertCoordNormalization(node, config);
|
||||||
|
node = InsertCoordGatherBias(node, config);
|
||||||
|
node = InsertConstOffsets(node, config);
|
||||||
|
|
||||||
if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat)
|
if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat)
|
||||||
{
|
{
|
||||||
@@ -344,10 +349,279 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LinkedListNode<INode> RewriteTextureSample(LinkedListNode<INode> node, ShaderConfig config)
|
private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
|
||||||
{
|
{
|
||||||
TextureOperation texOp = (TextureOperation)node.Value;
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||||
|
|
||||||
|
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
int coordsCount = texOp.Type.GetDimensions();
|
||||||
|
|
||||||
|
int coordsIndex = isBindless || isIndexed ? 1 : 0;
|
||||||
|
|
||||||
|
bool isImage = IsImageInstructionWithScale(texOp.Inst);
|
||||||
|
|
||||||
|
if ((texOp.Inst == Instruction.TextureSample || isImage) &&
|
||||||
|
intCoords &&
|
||||||
|
!isBindless &&
|
||||||
|
!isIndexed &&
|
||||||
|
config.Stage.SupportsRenderScale() &&
|
||||||
|
TypeSupportsScale(texOp.Type))
|
||||||
|
{
|
||||||
|
int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale);
|
||||||
|
int samplerIndex = isImage
|
||||||
|
? config.GetTextureDescriptors().Length + config.FindImageDescriptorIndex(texOp)
|
||||||
|
: config.FindTextureDescriptorIndex(texOp);
|
||||||
|
|
||||||
|
for (int index = 0; index < coordsCount; index++)
|
||||||
|
{
|
||||||
|
Operand scaledCoord = Local();
|
||||||
|
Operand[] callArgs;
|
||||||
|
|
||||||
|
if (config.Stage == ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex), Const(index) };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex) };
|
||||||
|
}
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.Call, 0, scaledCoord, callArgs));
|
||||||
|
|
||||||
|
texOp.SetSource(coordsIndex + index, scaledCoord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkedListNode<INode> InsertTextureSizeUnscale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
|
||||||
|
{
|
||||||
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||||
|
|
||||||
|
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
if (texOp.Inst == Instruction.TextureSize &&
|
||||||
|
texOp.Index < 2 &&
|
||||||
|
!isBindless &&
|
||||||
|
!isIndexed &&
|
||||||
|
config.Stage.SupportsRenderScale() &&
|
||||||
|
TypeSupportsScale(texOp.Type))
|
||||||
|
{
|
||||||
|
int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale);
|
||||||
|
int samplerIndex = config.FindTextureDescriptorIndex(texOp, ignoreType: true);
|
||||||
|
|
||||||
|
for (int index = texOp.DestsCount - 1; index >= 0; index--)
|
||||||
|
{
|
||||||
|
Operand dest = texOp.GetDest(index);
|
||||||
|
|
||||||
|
Operand unscaledSize = Local();
|
||||||
|
|
||||||
|
// Replace all uses with the unscaled size value.
|
||||||
|
// This must be done before the call is added, since it also is a use of the original size.
|
||||||
|
foreach (INode useOp in dest.UseOps)
|
||||||
|
{
|
||||||
|
for (int srcIndex = 0; srcIndex < useOp.SourcesCount; srcIndex++)
|
||||||
|
{
|
||||||
|
if (useOp.GetSource(srcIndex) == dest)
|
||||||
|
{
|
||||||
|
useOp.SetSource(srcIndex, unscaledSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand[] callArgs = new Operand[] { Const(functionId), dest, Const(samplerIndex) };
|
||||||
|
|
||||||
|
node.List.AddAfter(node, new Operation(Instruction.Call, 0, unscaledSize, callArgs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsImageInstructionWithScale(Instruction inst)
|
||||||
|
{
|
||||||
|
// Currently, we don't support scaling images that are modified,
|
||||||
|
// so we only need to care about the load instruction.
|
||||||
|
return inst == Instruction.ImageLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TypeSupportsScale(SamplerType type)
|
||||||
|
{
|
||||||
|
return (type & SamplerType.Mask) == SamplerType.Texture2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkedListNode<INode> InsertCoordNormalization(LinkedListNode<INode> node, ShaderConfig config)
|
||||||
|
{
|
||||||
|
// Emulate non-normalized coordinates by normalizing the coordinates on the shader.
|
||||||
|
// Without normalization, the coordinates are expected to the in the [0, W or H] range,
|
||||||
|
// and otherwise, it is expected to be in the [0, 1] range.
|
||||||
|
// We normalize by dividing the coords by the texture size.
|
||||||
|
|
||||||
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||||
|
|
||||||
|
bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot);
|
||||||
|
|
||||||
|
if (isCoordNormalized || intCoords)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
int coordsCount = texOp.Type.GetDimensions();
|
||||||
|
int coordsIndex = isBindless || isIndexed ? 1 : 0;
|
||||||
|
|
||||||
|
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||||
|
|
||||||
|
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
|
||||||
|
|
||||||
|
for (int index = 0; index < normCoordsCount; index++)
|
||||||
|
{
|
||||||
|
Operand coordSize = Local();
|
||||||
|
|
||||||
|
Operand[] texSizeSources;
|
||||||
|
|
||||||
|
if (isBindless || isIndexed)
|
||||||
|
{
|
||||||
|
texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
texSizeSources = new Operand[] { Const(0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
|
Instruction.TextureSize,
|
||||||
|
texOp.Type,
|
||||||
|
texOp.Format,
|
||||||
|
texOp.Flags,
|
||||||
|
texOp.CbufSlot,
|
||||||
|
texOp.Handle,
|
||||||
|
index,
|
||||||
|
new[] { coordSize },
|
||||||
|
texSizeSources));
|
||||||
|
|
||||||
|
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
||||||
|
|
||||||
|
Operand source = texOp.GetSource(coordsIndex + index);
|
||||||
|
|
||||||
|
Operand coordNormalized = Local();
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize)));
|
||||||
|
|
||||||
|
texOp.SetSource(coordsIndex + index, coordNormalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkedListNode<INode> InsertCoordGatherBias(LinkedListNode<INode> node, ShaderConfig config)
|
||||||
|
{
|
||||||
|
// The gather behavior when the coordinate sits right in the middle of two texels is not well defined.
|
||||||
|
// To ensure the correct texel is sampled, we add a small bias value to the coordinate.
|
||||||
|
// This value is calculated as the minimum value required to change the texel it will sample from,
|
||||||
|
// and is 0 if the host does not require the bias.
|
||||||
|
|
||||||
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
|
||||||
|
|
||||||
|
int gatherBiasPrecision = config.GpuAccessor.QueryHostGatherBiasPrecision();
|
||||||
|
|
||||||
|
if (!isGather || gatherBiasPrecision == 0)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||||
|
|
||||||
|
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
int coordsCount = texOp.Type.GetDimensions();
|
||||||
|
int coordsIndex = isBindless || isIndexed ? 1 : 0;
|
||||||
|
|
||||||
|
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||||
|
|
||||||
|
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
|
||||||
|
|
||||||
|
for (int index = 0; index < normCoordsCount; index++)
|
||||||
|
{
|
||||||
|
Operand coordSize = Local();
|
||||||
|
Operand scaledSize = Local();
|
||||||
|
Operand bias = Local();
|
||||||
|
|
||||||
|
Operand[] texSizeSources;
|
||||||
|
|
||||||
|
if (isBindless || isIndexed)
|
||||||
|
{
|
||||||
|
texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
texSizeSources = new Operand[] { Const(0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
|
Instruction.TextureSize,
|
||||||
|
texOp.Type,
|
||||||
|
texOp.Format,
|
||||||
|
texOp.Flags,
|
||||||
|
texOp.CbufSlot,
|
||||||
|
texOp.Handle,
|
||||||
|
index,
|
||||||
|
new[] { coordSize },
|
||||||
|
texSizeSources));
|
||||||
|
|
||||||
|
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(
|
||||||
|
Instruction.FP32 | Instruction.Multiply,
|
||||||
|
scaledSize,
|
||||||
|
GenerateI2f(node, coordSize),
|
||||||
|
ConstF((float)(1 << (gatherBiasPrecision + 1)))));
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, bias, ConstF(1f), scaledSize));
|
||||||
|
|
||||||
|
Operand source = texOp.GetSource(coordsIndex + index);
|
||||||
|
|
||||||
|
Operand coordBiased = Local();
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordBiased, source, bias));
|
||||||
|
|
||||||
|
texOp.SetSource(coordsIndex + index, coordBiased);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ShaderConfig config)
|
||||||
|
{
|
||||||
|
// Non-constant texture offsets are not allowed (according to the spec),
|
||||||
|
// however some GPUs does support that.
|
||||||
|
// For GPUs where it is not supported, we can replace the instruction with the following:
|
||||||
|
// For texture*Offset, we replace it by texture*, and add the offset to the P coords.
|
||||||
|
// The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords).
|
||||||
|
// For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly.
|
||||||
|
// For textureGatherOffset, we split the operation into up to 4 operations, one for each component
|
||||||
|
// that is accessed, where each textureGather operation has a different offset for each pixel.
|
||||||
|
|
||||||
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
|
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
|
||||||
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
|
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
|
||||||
|
|
||||||
@@ -355,9 +629,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
|
||||||
bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot);
|
if (!hasInvalidOffset)
|
||||||
|
|
||||||
if (!hasInvalidOffset && isCoordNormalized)
|
|
||||||
{
|
{
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -454,7 +726,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
hasInvalidOffset &= !areAllOffsetsConstant;
|
hasInvalidOffset &= !areAllOffsetsConstant;
|
||||||
|
|
||||||
if (!hasInvalidOffset && isCoordNormalized)
|
if (!hasInvalidOffset)
|
||||||
{
|
{
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -473,63 +745,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
int componentIndex = texOp.Index;
|
int componentIndex = texOp.Index;
|
||||||
|
|
||||||
Operand Float(Operand value)
|
|
||||||
{
|
|
||||||
Operand res = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value));
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emulate non-normalized coordinates by normalizing the coordinates on the shader.
|
|
||||||
// Without normalization, the coordinates are expected to the in the [0, W or H] range,
|
|
||||||
// and otherwise, it is expected to be in the [0, 1] range.
|
|
||||||
// We normalize by dividing the coords by the texture size.
|
|
||||||
if (!isCoordNormalized && !intCoords)
|
|
||||||
{
|
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
|
||||||
|
|
||||||
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
|
|
||||||
|
|
||||||
for (int index = 0; index < normCoordsCount; index++)
|
|
||||||
{
|
|
||||||
Operand coordSize = Local();
|
|
||||||
|
|
||||||
Operand[] texSizeSources;
|
|
||||||
|
|
||||||
if (isBindless || isIndexed)
|
|
||||||
{
|
|
||||||
texSizeSources = new Operand[] { sources[0], Const(0) };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
texSizeSources = new Operand[] { Const(0) };
|
|
||||||
}
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
|
||||||
Instruction.TextureSize,
|
|
||||||
texOp.Type,
|
|
||||||
texOp.Format,
|
|
||||||
texOp.Flags,
|
|
||||||
texOp.CbufSlot,
|
|
||||||
texOp.Handle,
|
|
||||||
index,
|
|
||||||
new[] { coordSize },
|
|
||||||
texSizeSources));
|
|
||||||
|
|
||||||
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
|
||||||
|
|
||||||
Operand coordNormalized = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, Float(coordSize)));
|
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordNormalized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] dests = new Operand[texOp.DestsCount];
|
Operand[] dests = new Operand[texOp.DestsCount];
|
||||||
|
|
||||||
for (int i = 0; i < texOp.DestsCount; i++)
|
for (int i = 0; i < texOp.DestsCount; i++)
|
||||||
@@ -541,15 +756,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
LinkedListNode<INode> oldNode = node;
|
LinkedListNode<INode> oldNode = node;
|
||||||
|
|
||||||
// Technically, non-constant texture offsets are not allowed (according to the spec),
|
if (isGather && !isShadow)
|
||||||
// however some GPUs does support that.
|
|
||||||
// For GPUs where it is not supported, we can replace the instruction with the following:
|
|
||||||
// For texture*Offset, we replace it by texture*, and add the offset to the P coords.
|
|
||||||
// The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords).
|
|
||||||
// For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly.
|
|
||||||
// For textureGatherOffset, we split the operation into up to 4 operations, one for each component
|
|
||||||
// that is accessed, where each textureGather operation has a different offset for each pixel.
|
|
||||||
if (hasInvalidOffset && isGather && !isShadow)
|
|
||||||
{
|
{
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||||
|
|
||||||
@@ -557,7 +764,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
sources.CopyTo(newSources, 0);
|
sources.CopyTo(newSources, 0);
|
||||||
|
|
||||||
Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
|
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount);
|
||||||
|
|
||||||
int destIndex = 0;
|
int destIndex = 0;
|
||||||
|
|
||||||
@@ -576,7 +783,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
|
Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
|
node.List.AddBefore(node, new Operation(
|
||||||
|
Instruction.FP32 | Instruction.Divide,
|
||||||
|
offset,
|
||||||
|
GenerateI2f(node, intOffset),
|
||||||
|
GenerateI2f(node, texSizes[index])));
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
Operand source = sources[coordsIndex + index];
|
||||||
|
|
||||||
@@ -603,45 +814,46 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (hasInvalidOffset)
|
if (intCoords)
|
||||||
{
|
{
|
||||||
if (intCoords)
|
for (int index = 0; index < coordsCount; index++)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < coordsCount; index++)
|
Operand source = sources[coordsIndex + index];
|
||||||
{
|
|
||||||
Operand source = sources[coordsIndex + index];
|
|
||||||
|
|
||||||
Operand coordPlusOffset = Local();
|
Operand coordPlusOffset = Local();
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
|
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordPlusOffset;
|
sources[coordsIndex + index] = coordPlusOffset;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||||
|
|
||||||
|
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount);
|
||||||
|
|
||||||
|
for (int index = 0; index < coordsCount; index++)
|
||||||
{
|
{
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
||||||
|
|
||||||
Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
|
Operand offset = Local();
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
Operand intOffset = offsets[index];
|
||||||
{
|
|
||||||
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
|
||||||
|
|
||||||
Operand offset = Local();
|
node.List.AddBefore(node, new Operation(
|
||||||
|
Instruction.FP32 | Instruction.Divide,
|
||||||
|
offset,
|
||||||
|
GenerateI2f(node, intOffset),
|
||||||
|
GenerateI2f(node, texSizes[index])));
|
||||||
|
|
||||||
Operand intOffset = offsets[index];
|
Operand source = sources[coordsIndex + index];
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
|
Operand coordPlusOffset = Local();
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
|
||||||
|
|
||||||
Operand coordPlusOffset = Local();
|
sources[coordsIndex + index] = coordPlusOffset;
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
|
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordPlusOffset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,22 +881,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Operand[] InsertTextureSize(
|
private static Operand[] InsertTextureLod(
|
||||||
LinkedListNode<INode> node,
|
LinkedListNode<INode> node,
|
||||||
TextureOperation texOp,
|
TextureOperation texOp,
|
||||||
Operand[] lodSources,
|
Operand[] lodSources,
|
||||||
Operand bindlessHandle,
|
Operand bindlessHandle,
|
||||||
int coordsCount)
|
int coordsCount)
|
||||||
{
|
{
|
||||||
Operand Int(Operand value)
|
|
||||||
{
|
|
||||||
Operand res = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value));
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] texSizes = new Operand[coordsCount];
|
Operand[] texSizes = new Operand[coordsCount];
|
||||||
|
|
||||||
Operand lod = Local();
|
Operand lod = Local();
|
||||||
@@ -708,11 +911,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
if (bindlessHandle != null)
|
if (bindlessHandle != null)
|
||||||
{
|
{
|
||||||
texSizeSources = new Operand[] { bindlessHandle, Int(lod) };
|
texSizeSources = new Operand[] { bindlessHandle, GenerateF2i(node, lod) };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
texSizeSources = new Operand[] { Int(lod) };
|
texSizeSources = new Operand[] { GenerateF2i(node, lod) };
|
||||||
}
|
}
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
@@ -796,6 +999,24 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand GenerateI2f(LinkedListNode<INode> node, Operand value)
|
||||||
|
{
|
||||||
|
Operand res = Local();
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand GenerateF2i(LinkedListNode<INode> node, Operand value)
|
||||||
|
{
|
||||||
|
Operand res = Local();
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation)
|
private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation)
|
||||||
{
|
{
|
||||||
Operand GenerateLoad(IoVariable ioVariable)
|
Operand GenerateLoad(IoVariable ioVariable)
|
||||||
|
@@ -860,7 +860,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return descriptors;
|
return descriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp)
|
public TextureDescriptor FindTextureDescriptor(AstTextureOperation texOp)
|
||||||
{
|
{
|
||||||
TextureDescriptor[] descriptors = GetTextureDescriptors();
|
TextureDescriptor[] descriptors = GetTextureDescriptors();
|
||||||
|
|
||||||
@@ -872,11 +872,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
descriptor.HandleIndex == texOp.Handle &&
|
descriptor.HandleIndex == texOp.Handle &&
|
||||||
descriptor.Format == texOp.Format)
|
descriptor.Format == texOp.Format)
|
||||||
{
|
{
|
||||||
return (descriptor, i);
|
return descriptor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (default, -1);
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
|
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
|
||||||
@@ -897,12 +897,30 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FindTextureDescriptorIndex(AstTextureOperation texOp)
|
private static int FindDescriptorIndex(TextureDescriptor[] array, TextureOperation texOp, bool ignoreType = false)
|
||||||
{
|
{
|
||||||
return FindDescriptorIndex(GetTextureDescriptors(), texOp);
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
var descriptor = array[i];
|
||||||
|
|
||||||
|
if ((descriptor.Type == texOp.Type || ignoreType) &&
|
||||||
|
descriptor.CbufSlot == texOp.CbufSlot &&
|
||||||
|
descriptor.HandleIndex == texOp.Handle &&
|
||||||
|
descriptor.Format == texOp.Format)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FindImageDescriptorIndex(AstTextureOperation texOp)
|
public int FindTextureDescriptorIndex(TextureOperation texOp, bool ignoreType = false)
|
||||||
|
{
|
||||||
|
return FindDescriptorIndex(GetTextureDescriptors(), texOp, ignoreType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FindImageDescriptorIndex(TextureOperation texOp)
|
||||||
{
|
{
|
||||||
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
static class ShaderIdentifier
|
static class ShaderIdentifier
|
||||||
{
|
{
|
||||||
public static ShaderIdentification Identify(Function[] functions, ShaderConfig config)
|
public static ShaderIdentification Identify(IReadOnlyList<Function> functions, ShaderConfig config)
|
||||||
{
|
{
|
||||||
if (config.Stage == ShaderStage.Geometry &&
|
if (config.Stage == ShaderStage.Geometry &&
|
||||||
config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles &&
|
config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles &&
|
||||||
@@ -20,12 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return ShaderIdentification.None;
|
return ShaderIdentification.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsLayerPassthroughGeometryShader(Function[] functions, out int layerInputAttr)
|
private static bool IsLayerPassthroughGeometryShader(IReadOnlyList<Function> functions, out int layerInputAttr)
|
||||||
{
|
{
|
||||||
bool writesLayer = false;
|
bool writesLayer = false;
|
||||||
layerInputAttr = 0;
|
layerInputAttr = 0;
|
||||||
|
|
||||||
if (functions.Length != 1)
|
if (functions.Count != 1)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
@@ -44,7 +45,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Function[] funcs = new Function[functions.Length];
|
List<Function> funcs = new List<Function>(functions.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < functions.Length; i++)
|
||||||
|
{
|
||||||
|
funcs.Add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
HelperFunctionManager hfm = new HelperFunctionManager(funcs, config.Stage);
|
||||||
|
|
||||||
for (int i = 0; i < functions.Length; i++)
|
for (int i = 0; i < functions.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -71,7 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
Ssa.Rename(cfg.Blocks);
|
Ssa.Rename(cfg.Blocks);
|
||||||
|
|
||||||
Optimizer.RunPass(cfg.Blocks, config);
|
Optimizer.RunPass(cfg.Blocks, config);
|
||||||
Rewriter.RunPass(cfg.Blocks, config);
|
Rewriter.RunPass(hfm, cfg.Blocks, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
|
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
|
||||||
|
@@ -372,8 +372,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp)
|
private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp)
|
||||||
{
|
{
|
||||||
var program = _program;
|
var program = _program;
|
||||||
int stagesCount = program.Bindings[setIndex].Length;
|
var bindingSegments = program.BindingSegments[setIndex];
|
||||||
if (stagesCount == 0 && setIndex != PipelineBase.UniformSetIndex)
|
|
||||||
|
if (bindingSegments.Length == 0 && setIndex != PipelineBase.UniformSetIndex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -410,125 +411,113 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
|
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||||
{
|
{
|
||||||
var stageBindings = program.Bindings[setIndex][stageIndex];
|
int binding = segment.Binding;
|
||||||
int bindingsCount = stageBindings.Length;
|
int count = segment.Count;
|
||||||
int count;
|
|
||||||
|
|
||||||
for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
|
if (setIndex == PipelineBase.UniformSetIndex)
|
||||||
{
|
{
|
||||||
int binding = stageBindings[bindingIndex];
|
for (int i = 0; i < count; i++)
|
||||||
count = 1;
|
|
||||||
|
|
||||||
while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
|
|
||||||
{
|
{
|
||||||
count++;
|
int index = binding + i;
|
||||||
|
|
||||||
|
if (!_uniformSet[index])
|
||||||
|
{
|
||||||
|
UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
|
||||||
|
|
||||||
|
_uniformSet[index] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setIndex == PipelineBase.UniformSetIndex)
|
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||||
|
dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||||
|
}
|
||||||
|
else if (setIndex == PipelineBase.StorageSetIndex)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
int index = binding + i;
|
||||||
|
|
||||||
|
if (!_storageSet[index])
|
||||||
|
{
|
||||||
|
UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer);
|
||||||
|
|
||||||
|
_storageSet[index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
||||||
|
if (program.HasMinimalLayout)
|
||||||
|
{
|
||||||
|
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||||
|
{
|
||||||
|
if (segment.Type != ResourceType.BufferTexture)
|
||||||
|
{
|
||||||
|
Span<DescriptorImageInfo> textures = _textures;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
int index = binding + i;
|
ref var texture = ref textures[i];
|
||||||
|
|
||||||
if (!_uniformSet[index])
|
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||||
|
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
|
if (texture.ImageView.Handle == 0)
|
||||||
{
|
{
|
||||||
UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
|
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
||||||
|
}
|
||||||
|
|
||||||
_uniformSet[index] = true;
|
if (texture.Sampler.Handle == 0)
|
||||||
|
{
|
||||||
|
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler);
|
||||||
dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
|
||||||
}
|
}
|
||||||
else if (setIndex == PipelineBase.StorageSetIndex)
|
else
|
||||||
{
|
{
|
||||||
|
Span<BufferView> bufferTextures = _bufferTextures;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
int index = binding + i;
|
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
|
||||||
|
|
||||||
if (!_storageSet[index])
|
|
||||||
{
|
|
||||||
UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer);
|
|
||||||
|
|
||||||
_storageSet[index] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer);
|
||||||
if (program.HasMinimalLayout)
|
|
||||||
{
|
|
||||||
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
}
|
||||||
|
else if (setIndex == PipelineBase.ImageSetIndex)
|
||||||
|
{
|
||||||
|
if (segment.Type != ResourceType.BufferImage)
|
||||||
{
|
{
|
||||||
if (((uint)binding % (Constants.MaxTexturesPerStage * 2)) < Constants.MaxTexturesPerStage || program.HasMinimalLayout)
|
Span<DescriptorImageInfo> images = _images;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Span<DescriptorImageInfo> textures = _textures;
|
images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
ref var texture = ref textures[i];
|
|
||||||
|
|
||||||
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
|
|
||||||
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
|
|
||||||
|
|
||||||
if (texture.ImageView.Handle == 0)
|
|
||||||
{
|
|
||||||
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture.Sampler.Handle == 0)
|
|
||||||
{
|
|
||||||
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Span<BufferView> bufferTextures = _bufferTextures;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage);
|
||||||
{
|
|
||||||
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (setIndex == PipelineBase.ImageSetIndex)
|
else
|
||||||
{
|
{
|
||||||
if (((uint)binding % (Constants.MaxImagesPerStage * 2)) < Constants.MaxImagesPerStage || program.HasMinimalLayout)
|
Span<BufferView> bufferImages = _bufferImages;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Span<DescriptorImageInfo> images = _images;
|
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Span<BufferView> bufferImages = _bufferImages;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer);
|
||||||
{
|
|
||||||
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -568,9 +557,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
|
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
|
||||||
{
|
{
|
||||||
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
|
||||||
int stagesCount = _program.Bindings[PipelineBase.UniformSetIndex].Length;
|
|
||||||
|
|
||||||
if (!_uniformSet[0])
|
if (!_uniformSet[0])
|
||||||
{
|
{
|
||||||
Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1];
|
Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1];
|
||||||
@@ -587,41 +573,32 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer);
|
UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
|
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
|
||||||
|
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||||
|
|
||||||
|
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||||
{
|
{
|
||||||
var stageBindings = _program.Bindings[PipelineBase.UniformSetIndex][stageIndex];
|
int binding = segment.Binding;
|
||||||
int bindingsCount = stageBindings.Length;
|
int count = segment.Count;
|
||||||
int count;
|
|
||||||
|
|
||||||
for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
|
bool doUpdate = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
int binding = stageBindings[bindingIndex];
|
int index = binding + i;
|
||||||
count = 1;
|
|
||||||
|
|
||||||
while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
|
if (!_uniformSet[index])
|
||||||
{
|
{
|
||||||
count++;
|
UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
|
||||||
|
_uniformSet[index] = true;
|
||||||
|
doUpdate = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool doUpdate = false;
|
if (doUpdate)
|
||||||
|
{
|
||||||
for (int i = 0; i < count; i++)
|
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||||
{
|
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||||
int index = binding + i;
|
|
||||||
|
|
||||||
if (!_uniformSet[index])
|
|
||||||
{
|
|
||||||
UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
|
|
||||||
_uniformSet[index] = true;
|
|
||||||
doUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doUpdate)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
|
||||||
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -54,29 +54,29 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv");
|
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv");
|
||||||
var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv");
|
var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv");
|
||||||
|
|
||||||
var computeBindings = new ShaderBindings(
|
var scalingResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
new[] { 0 });
|
|
||||||
|
|
||||||
var sharpeningBindings = new ShaderBindings(
|
var sharpeningResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2, 3, 4 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
|
||||||
new[] { 0 });
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
|
|
||||||
_sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
_scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(scalingShader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
});
|
}, scalingResourceLayout);
|
||||||
|
|
||||||
_sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(sharpeningShader, sharpeningBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(sharpeningShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
});
|
}, sharpeningResourceLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run(
|
public void Run(
|
||||||
@@ -160,10 +160,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
// Sharpening pass
|
// Sharpening pass
|
||||||
_pipeline.SetCommandBuffer(cbs);
|
|
||||||
_pipeline.SetProgram(_sharpeningProgram);
|
_pipeline.SetProgram(_sharpeningProgram);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler);
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
|
||||||
var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float));
|
var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float));
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) });
|
||||||
_pipeline.SetImage(0, destinationTexture);
|
_pipeline.SetImage(0, destinationTexture);
|
||||||
|
@@ -38,18 +38,17 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
|
|
||||||
var shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv");
|
var shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv");
|
||||||
|
|
||||||
var computeBindings = new ShaderBindings(
|
var resourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
new[] { 0 });
|
|
||||||
|
|
||||||
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
_shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(shader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(shader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
});
|
}, resourceLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height)
|
public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height)
|
||||||
|
@@ -77,23 +77,23 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv");
|
var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv");
|
||||||
var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv");
|
var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv");
|
||||||
|
|
||||||
var edgeBindings = new ShaderBindings(
|
var edgeResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
new[] { 0 });
|
|
||||||
|
|
||||||
var blendBindings = new ShaderBindings(
|
var blendResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1, 3, 4 },
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
|
||||||
new[] { 0 });
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
|
||||||
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
|
|
||||||
var neighbourBindings = new ShaderBindings(
|
var neighbourResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1, 3 },
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
|
||||||
new[] { 0 });
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
|
|
||||||
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
@@ -117,18 +117,18 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
|
|
||||||
_edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(edgeShader, edgeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(edgeShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
}, new[] { specInfo });
|
}, edgeResourceLayout, new[] { specInfo });
|
||||||
|
|
||||||
_blendProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_blendProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(blendShader, blendBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(blendShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
}, new[] { specInfo });
|
}, blendResourceLayout, new[] { specInfo });
|
||||||
|
|
||||||
_neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(neighbourShader, neighbourBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(neighbourShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
}, new[] { specInfo });
|
}, neighbourResourceLayout, new[] { specInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeletePipelines()
|
public void DeletePipelines()
|
||||||
|
@@ -36,6 +36,56 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ShaderStageFlags Convert(this ResourceStages stages)
|
||||||
|
{
|
||||||
|
ShaderStageFlags stageFlags = stages.HasFlag(ResourceStages.Compute)
|
||||||
|
? ShaderStageFlags.ComputeBit
|
||||||
|
: ShaderStageFlags.None;
|
||||||
|
|
||||||
|
if (stages.HasFlag(ResourceStages.Vertex))
|
||||||
|
{
|
||||||
|
stageFlags |= ShaderStageFlags.VertexBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stages.HasFlag(ResourceStages.TessellationControl))
|
||||||
|
{
|
||||||
|
stageFlags |= ShaderStageFlags.TessellationControlBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stages.HasFlag(ResourceStages.TessellationEvaluation))
|
||||||
|
{
|
||||||
|
stageFlags |= ShaderStageFlags.TessellationEvaluationBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stages.HasFlag(ResourceStages.Geometry))
|
||||||
|
{
|
||||||
|
stageFlags |= ShaderStageFlags.GeometryBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stages.HasFlag(ResourceStages.Fragment))
|
||||||
|
{
|
||||||
|
stageFlags |= ShaderStageFlags.FragmentBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stageFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DescriptorType Convert(this ResourceType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
ResourceType.UniformBuffer => DescriptorType.UniformBuffer,
|
||||||
|
ResourceType.StorageBuffer => DescriptorType.StorageBuffer,
|
||||||
|
ResourceType.Texture => DescriptorType.SampledImage,
|
||||||
|
ResourceType.Sampler => DescriptorType.Sampler,
|
||||||
|
ResourceType.TextureAndSampler => DescriptorType.CombinedImageSampler,
|
||||||
|
ResourceType.Image => DescriptorType.StorageImage,
|
||||||
|
ResourceType.BufferTexture => DescriptorType.UniformTexelBuffer,
|
||||||
|
ResourceType.BufferImage => DescriptorType.StorageTexelBuffer,
|
||||||
|
_ => throw new ArgumentException($"Invalid resource type \"{type}\".")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static SamplerAddressMode Convert(this AddressMode mode)
|
public static SamplerAddressMode Convert(this AddressMode mode)
|
||||||
{
|
{
|
||||||
return mode switch
|
return mode switch
|
||||||
@@ -48,7 +98,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder,
|
AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder,
|
||||||
AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat,
|
AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat,
|
||||||
AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge,
|
AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge,
|
||||||
_ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp.
|
_ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -168,5 +168,223 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return _table[(int)format];
|
return _table[(int)format];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetAttributeFormatSize(VkFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case VkFormat.R8Unorm:
|
||||||
|
case VkFormat.R8SNorm:
|
||||||
|
case VkFormat.R8Uint:
|
||||||
|
case VkFormat.R8Sint:
|
||||||
|
case VkFormat.R8Uscaled:
|
||||||
|
case VkFormat.R8Sscaled:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case VkFormat.R8G8Unorm:
|
||||||
|
case VkFormat.R8G8SNorm:
|
||||||
|
case VkFormat.R8G8Uint:
|
||||||
|
case VkFormat.R8G8Sint:
|
||||||
|
case VkFormat.R8G8Uscaled:
|
||||||
|
case VkFormat.R8G8Sscaled:
|
||||||
|
case VkFormat.R16Sfloat:
|
||||||
|
case VkFormat.R16Unorm:
|
||||||
|
case VkFormat.R16SNorm:
|
||||||
|
case VkFormat.R16Uint:
|
||||||
|
case VkFormat.R16Sint:
|
||||||
|
case VkFormat.R16Uscaled:
|
||||||
|
case VkFormat.R16Sscaled:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case VkFormat.R8G8B8Unorm:
|
||||||
|
case VkFormat.R8G8B8SNorm:
|
||||||
|
case VkFormat.R8G8B8Uint:
|
||||||
|
case VkFormat.R8G8B8Sint:
|
||||||
|
case VkFormat.R8G8B8Uscaled:
|
||||||
|
case VkFormat.R8G8B8Sscaled:
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
case VkFormat.R8G8B8A8Unorm:
|
||||||
|
case VkFormat.R8G8B8A8SNorm:
|
||||||
|
case VkFormat.R8G8B8A8Uint:
|
||||||
|
case VkFormat.R8G8B8A8Sint:
|
||||||
|
case VkFormat.R8G8B8A8Srgb:
|
||||||
|
case VkFormat.R8G8B8A8Uscaled:
|
||||||
|
case VkFormat.R8G8B8A8Sscaled:
|
||||||
|
case VkFormat.B8G8R8A8Unorm:
|
||||||
|
case VkFormat.B8G8R8A8Srgb:
|
||||||
|
case VkFormat.R16G16Sfloat:
|
||||||
|
case VkFormat.R16G16Unorm:
|
||||||
|
case VkFormat.R16G16SNorm:
|
||||||
|
case VkFormat.R16G16Uint:
|
||||||
|
case VkFormat.R16G16Sint:
|
||||||
|
case VkFormat.R16G16Uscaled:
|
||||||
|
case VkFormat.R16G16Sscaled:
|
||||||
|
case VkFormat.R32Sfloat:
|
||||||
|
case VkFormat.R32Uint:
|
||||||
|
case VkFormat.R32Sint:
|
||||||
|
case VkFormat.A2B10G10R10UnormPack32:
|
||||||
|
case VkFormat.A2B10G10R10UintPack32:
|
||||||
|
case VkFormat.B10G11R11UfloatPack32:
|
||||||
|
case VkFormat.E5B9G9R9UfloatPack32:
|
||||||
|
case VkFormat.A2B10G10R10SNormPack32:
|
||||||
|
case VkFormat.A2B10G10R10SintPack32:
|
||||||
|
case VkFormat.A2B10G10R10UscaledPack32:
|
||||||
|
case VkFormat.A2B10G10R10SscaledPack32:
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case VkFormat.R16G16B16Sfloat:
|
||||||
|
case VkFormat.R16G16B16Unorm:
|
||||||
|
case VkFormat.R16G16B16SNorm:
|
||||||
|
case VkFormat.R16G16B16Uint:
|
||||||
|
case VkFormat.R16G16B16Sint:
|
||||||
|
case VkFormat.R16G16B16Uscaled:
|
||||||
|
case VkFormat.R16G16B16Sscaled:
|
||||||
|
return 6;
|
||||||
|
|
||||||
|
case VkFormat.R16G16B16A16Sfloat:
|
||||||
|
case VkFormat.R16G16B16A16Unorm:
|
||||||
|
case VkFormat.R16G16B16A16SNorm:
|
||||||
|
case VkFormat.R16G16B16A16Uint:
|
||||||
|
case VkFormat.R16G16B16A16Sint:
|
||||||
|
case VkFormat.R16G16B16A16Uscaled:
|
||||||
|
case VkFormat.R16G16B16A16Sscaled:
|
||||||
|
case VkFormat.R32G32Sfloat:
|
||||||
|
case VkFormat.R32G32Uint:
|
||||||
|
case VkFormat.R32G32Sint:
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
case VkFormat.R32G32B32Sfloat:
|
||||||
|
case VkFormat.R32G32B32Uint:
|
||||||
|
case VkFormat.R32G32B32Sint:
|
||||||
|
return 12;
|
||||||
|
|
||||||
|
case VkFormat.R32G32B32A32Sfloat:
|
||||||
|
case VkFormat.R32G32B32A32Uint:
|
||||||
|
case VkFormat.R32G32B32A32Sint:
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VkFormat DropLastComponent(VkFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case VkFormat.R8G8Unorm:
|
||||||
|
return VkFormat.R8Unorm;
|
||||||
|
case VkFormat.R8G8SNorm:
|
||||||
|
return VkFormat.R8SNorm;
|
||||||
|
case VkFormat.R8G8Uint:
|
||||||
|
return VkFormat.R8Uint;
|
||||||
|
case VkFormat.R8G8Sint:
|
||||||
|
return VkFormat.R8Sint;
|
||||||
|
case VkFormat.R8G8Uscaled:
|
||||||
|
return VkFormat.R8Uscaled;
|
||||||
|
case VkFormat.R8G8Sscaled:
|
||||||
|
return VkFormat.R8Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R8G8B8Unorm:
|
||||||
|
return VkFormat.R8G8Unorm;
|
||||||
|
case VkFormat.R8G8B8SNorm:
|
||||||
|
return VkFormat.R8G8SNorm;
|
||||||
|
case VkFormat.R8G8B8Uint:
|
||||||
|
return VkFormat.R8G8Uint;
|
||||||
|
case VkFormat.R8G8B8Sint:
|
||||||
|
return VkFormat.R8G8Sint;
|
||||||
|
case VkFormat.R8G8B8Uscaled:
|
||||||
|
return VkFormat.R8G8Uscaled;
|
||||||
|
case VkFormat.R8G8B8Sscaled:
|
||||||
|
return VkFormat.R8G8Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R8G8B8A8Unorm:
|
||||||
|
return VkFormat.R8G8B8Unorm;
|
||||||
|
case VkFormat.R8G8B8A8SNorm:
|
||||||
|
return VkFormat.R8G8B8SNorm;
|
||||||
|
case VkFormat.R8G8B8A8Uint:
|
||||||
|
return VkFormat.R8G8B8Uint;
|
||||||
|
case VkFormat.R8G8B8A8Sint:
|
||||||
|
return VkFormat.R8G8B8Sint;
|
||||||
|
case VkFormat.R8G8B8A8Srgb:
|
||||||
|
return VkFormat.R8G8B8Srgb;
|
||||||
|
case VkFormat.R8G8B8A8Uscaled:
|
||||||
|
return VkFormat.R8G8B8Uscaled;
|
||||||
|
case VkFormat.R8G8B8A8Sscaled:
|
||||||
|
return VkFormat.R8G8B8Sscaled;
|
||||||
|
case VkFormat.B8G8R8A8Unorm:
|
||||||
|
return VkFormat.B8G8R8Unorm;
|
||||||
|
case VkFormat.B8G8R8A8Srgb:
|
||||||
|
return VkFormat.B8G8R8Srgb;
|
||||||
|
|
||||||
|
case VkFormat.R16G16Sfloat:
|
||||||
|
return VkFormat.R16Sfloat;
|
||||||
|
case VkFormat.R16G16Unorm:
|
||||||
|
return VkFormat.R16Unorm;
|
||||||
|
case VkFormat.R16G16SNorm:
|
||||||
|
return VkFormat.R16SNorm;
|
||||||
|
case VkFormat.R16G16Uint:
|
||||||
|
return VkFormat.R16Uint;
|
||||||
|
case VkFormat.R16G16Sint:
|
||||||
|
return VkFormat.R16Sint;
|
||||||
|
case VkFormat.R16G16Uscaled:
|
||||||
|
return VkFormat.R16Uscaled;
|
||||||
|
case VkFormat.R16G16Sscaled:
|
||||||
|
return VkFormat.R16Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R16G16B16Sfloat:
|
||||||
|
return VkFormat.R16G16Sfloat;
|
||||||
|
case VkFormat.R16G16B16Unorm:
|
||||||
|
return VkFormat.R16G16Unorm;
|
||||||
|
case VkFormat.R16G16B16SNorm:
|
||||||
|
return VkFormat.R16G16SNorm;
|
||||||
|
case VkFormat.R16G16B16Uint:
|
||||||
|
return VkFormat.R16G16Uint;
|
||||||
|
case VkFormat.R16G16B16Sint:
|
||||||
|
return VkFormat.R16G16Sint;
|
||||||
|
case VkFormat.R16G16B16Uscaled:
|
||||||
|
return VkFormat.R16G16Uscaled;
|
||||||
|
case VkFormat.R16G16B16Sscaled:
|
||||||
|
return VkFormat.R16G16Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R16G16B16A16Sfloat:
|
||||||
|
return VkFormat.R16G16B16Sfloat;
|
||||||
|
case VkFormat.R16G16B16A16Unorm:
|
||||||
|
return VkFormat.R16G16B16Unorm;
|
||||||
|
case VkFormat.R16G16B16A16SNorm:
|
||||||
|
return VkFormat.R16G16B16SNorm;
|
||||||
|
case VkFormat.R16G16B16A16Uint:
|
||||||
|
return VkFormat.R16G16B16Uint;
|
||||||
|
case VkFormat.R16G16B16A16Sint:
|
||||||
|
return VkFormat.R16G16B16Sint;
|
||||||
|
case VkFormat.R16G16B16A16Uscaled:
|
||||||
|
return VkFormat.R16G16B16Uscaled;
|
||||||
|
case VkFormat.R16G16B16A16Sscaled:
|
||||||
|
return VkFormat.R16G16B16Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R32G32Sfloat:
|
||||||
|
return VkFormat.R32Sfloat;
|
||||||
|
case VkFormat.R32G32Uint:
|
||||||
|
return VkFormat.R32Uint;
|
||||||
|
case VkFormat.R32G32Sint:
|
||||||
|
return VkFormat.R32Sint;
|
||||||
|
|
||||||
|
case VkFormat.R32G32B32Sfloat:
|
||||||
|
return VkFormat.R32G32Sfloat;
|
||||||
|
case VkFormat.R32G32B32Uint:
|
||||||
|
return VkFormat.R32G32Uint;
|
||||||
|
case VkFormat.R32G32B32Sint:
|
||||||
|
return VkFormat.R32G32Sint;
|
||||||
|
|
||||||
|
case VkFormat.R32G32B32A32Sfloat:
|
||||||
|
return VkFormat.R32G32B32Sfloat;
|
||||||
|
case VkFormat.R32G32B32A32Uint:
|
||||||
|
return VkFormat.R32G32B32Uint;
|
||||||
|
case VkFormat.R32G32B32A32Sint:
|
||||||
|
return VkFormat.R32G32B32Sint;
|
||||||
|
}
|
||||||
|
|
||||||
|
return format;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly bool SupportsSubgroupSizeControl;
|
public readonly bool SupportsSubgroupSizeControl;
|
||||||
public readonly bool SupportsShaderInt8;
|
public readonly bool SupportsShaderInt8;
|
||||||
public readonly bool SupportsShaderStencilExport;
|
public readonly bool SupportsShaderStencilExport;
|
||||||
|
public readonly bool SupportsShaderStorageImageMultisample;
|
||||||
public readonly bool SupportsConditionalRendering;
|
public readonly bool SupportsConditionalRendering;
|
||||||
public readonly bool SupportsExtendedDynamicState;
|
public readonly bool SupportsExtendedDynamicState;
|
||||||
public readonly bool SupportsMultiView;
|
public readonly bool SupportsMultiView;
|
||||||
@@ -63,6 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool supportsSubgroupSizeControl,
|
bool supportsSubgroupSizeControl,
|
||||||
bool supportsShaderInt8,
|
bool supportsShaderInt8,
|
||||||
bool supportsShaderStencilExport,
|
bool supportsShaderStencilExport,
|
||||||
|
bool supportsShaderStorageImageMultisample,
|
||||||
bool supportsConditionalRendering,
|
bool supportsConditionalRendering,
|
||||||
bool supportsExtendedDynamicState,
|
bool supportsExtendedDynamicState,
|
||||||
bool supportsMultiView,
|
bool supportsMultiView,
|
||||||
@@ -97,6 +99,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
|
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
|
||||||
SupportsShaderInt8 = supportsShaderInt8;
|
SupportsShaderInt8 = supportsShaderInt8;
|
||||||
SupportsShaderStencilExport = supportsShaderStencilExport;
|
SupportsShaderStencilExport = supportsShaderStencilExport;
|
||||||
|
SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample;
|
||||||
SupportsConditionalRendering = supportsConditionalRendering;
|
SupportsConditionalRendering = supportsConditionalRendering;
|
||||||
SupportsExtendedDynamicState = supportsExtendedDynamicState;
|
SupportsExtendedDynamicState = supportsExtendedDynamicState;
|
||||||
SupportsMultiView = supportsMultiView;
|
SupportsMultiView = supportsMultiView;
|
||||||
|
@@ -55,192 +55,168 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
||||||
|
|
||||||
var blitVertexBindings = new ShaderBindings(
|
var blitResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
var blitFragmentBindings = new ShaderBindings(
|
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>(),
|
|
||||||
new[] { 0 },
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
var colorClearFragmentBindings = new ShaderBindings(
|
var colorClearResourceLayout = new ResourceLayoutBuilder().Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1).Build();
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorClearResourceLayout);
|
||||||
|
|
||||||
_programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorClearResourceLayout);
|
||||||
|
|
||||||
_programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorClearResourceLayout);
|
||||||
|
|
||||||
var strideChangeBindings = new ShaderBindings(
|
var strideChangeResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
new[] { 1, 2 },
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
|
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||||
});
|
}, strideChangeResourceLayout);
|
||||||
|
|
||||||
var colorCopyBindings = new ShaderBindings(
|
var colorCopyResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
new[] { 0 });
|
|
||||||
|
|
||||||
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||||
});
|
}, colorCopyResourceLayout);
|
||||||
|
|
||||||
_programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||||
});
|
}, colorCopyResourceLayout);
|
||||||
|
|
||||||
_programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||||
});
|
}, colorCopyResourceLayout);
|
||||||
|
|
||||||
var colorDrawToMsVertexBindings = new ShaderBindings(
|
var colorDrawToMsResourceLayout = new ResourceLayoutBuilder()
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
var colorDrawToMsFragmentBindings = new ShaderBindings(
|
|
||||||
new[] { 0 },
|
|
||||||
Array.Empty<int>(),
|
|
||||||
new[] { 0 },
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
|
|
||||||
var convertD32S8ToD24S8Bindings = new ShaderBindings(
|
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
new[] { 1, 2 },
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
|
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, convertD32S8ToD24S8Bindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||||
});
|
}, convertD32S8ToD24S8ResourceLayout);
|
||||||
|
|
||||||
var convertIndexBufferBindings = new ShaderBindings(
|
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
new[] { 1, 2 },
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
|
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||||
});
|
}, convertIndexBufferResourceLayout);
|
||||||
|
|
||||||
var convertIndirectDataBindings = new ShaderBindings(
|
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
new[] { 1, 2, 3 },
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
|
||||||
Array.Empty<int>());
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
|
||||||
|
|
||||||
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
|
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||||
});
|
}, convertIndirectDataResourceLayout);
|
||||||
|
|
||||||
_programDepthBlit = gd.CreateProgramWithMinimalLayout(new[]
|
_programDepthBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
|
|
||||||
_programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
|
|
||||||
if (gd.Capabilities.SupportsShaderStencilExport)
|
if (gd.Capabilities.SupportsShaderStencilExport)
|
||||||
{
|
{
|
||||||
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
|
|
||||||
_programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,52 +1,101 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
class PipelineLayoutCache
|
class PipelineLayoutCache
|
||||||
{
|
{
|
||||||
private readonly PipelineLayoutCacheEntry[] _plce;
|
private readonly struct PlceKey : IEquatable<PlceKey>
|
||||||
private readonly List<PipelineLayoutCacheEntry> _plceMinimal;
|
{
|
||||||
|
public readonly ReadOnlyCollection<ResourceDescriptorCollection> SetDescriptors;
|
||||||
|
public readonly bool UsePushDescriptors;
|
||||||
|
|
||||||
|
public PlceKey(ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, bool usePushDescriptors)
|
||||||
|
{
|
||||||
|
SetDescriptors = setDescriptors;
|
||||||
|
UsePushDescriptors = usePushDescriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hasher = new HashCode();
|
||||||
|
|
||||||
|
if (SetDescriptors != null)
|
||||||
|
{
|
||||||
|
foreach (var setDescriptor in SetDescriptors)
|
||||||
|
{
|
||||||
|
hasher.Add(setDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher.Add(UsePushDescriptors);
|
||||||
|
|
||||||
|
return hasher.ToHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is PlceKey other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(PlceKey other)
|
||||||
|
{
|
||||||
|
if ((SetDescriptors == null) != (other.SetDescriptors == null))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SetDescriptors != null)
|
||||||
|
{
|
||||||
|
if (SetDescriptors.Count != other.SetDescriptors.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < SetDescriptors.Count; index++)
|
||||||
|
{
|
||||||
|
if (!SetDescriptors[index].Equals(other.SetDescriptors[index]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UsePushDescriptors == other.UsePushDescriptors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry> _plces;
|
||||||
|
|
||||||
public PipelineLayoutCache()
|
public PipelineLayoutCache()
|
||||||
{
|
{
|
||||||
_plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages];
|
_plces = new ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry>();
|
||||||
_plceMinimal = new List<PipelineLayoutCacheEntry>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry Create(VulkanRenderer gd, Device device, ShaderSource[] shaders)
|
public PipelineLayoutCacheEntry GetOrCreate(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
Device device,
|
||||||
|
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
||||||
|
bool usePushDescriptors)
|
||||||
{
|
{
|
||||||
var plce = new PipelineLayoutCacheEntry(gd, device, shaders);
|
var key = new PlceKey(setDescriptors, usePushDescriptors);
|
||||||
_plceMinimal.Add(plce);
|
|
||||||
return plce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry GetOrCreate(VulkanRenderer gd, Device device, uint stages, bool usePd)
|
return _plces.GetOrAdd(key, (newKey) => new PipelineLayoutCacheEntry(gd, device, setDescriptors, usePushDescriptors));
|
||||||
{
|
|
||||||
if (_plce[stages] == null)
|
|
||||||
{
|
|
||||||
_plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _plce[stages];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual unsafe void Dispose(bool disposing)
|
protected virtual unsafe void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _plce.Length; i++)
|
foreach (var plce in _plces.Values)
|
||||||
{
|
|
||||||
_plce[i]?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var plce in _plceMinimal)
|
|
||||||
{
|
{
|
||||||
plce.Dispose();
|
plce.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_plceMinimal.Clear();
|
_plces.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@@ -16,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly int[] _dsCacheCursor;
|
private readonly int[] _dsCacheCursor;
|
||||||
private int _dsLastCbIndex;
|
private int _dsLastCbIndex;
|
||||||
|
|
||||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device)
|
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
_device = device;
|
_device = device;
|
||||||
@@ -25,27 +26,24 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
|
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
|
||||||
{
|
{
|
||||||
_dsCache[i] = new List<Auto<DescriptorSetCollection>>[PipelineBase.DescriptorSetLayouts];
|
_dsCache[i] = new List<Auto<DescriptorSetCollection>>[setsCount];
|
||||||
|
|
||||||
for (int j = 0; j < PipelineBase.DescriptorSetLayouts; j++)
|
for (int j = 0; j < _dsCache[i].Length; j++)
|
||||||
{
|
{
|
||||||
_dsCache[i][j] = new List<Auto<DescriptorSetCollection>>();
|
_dsCache[i][j] = new List<Auto<DescriptorSetCollection>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts];
|
_dsCacheCursor = new int[setsCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, uint stages, bool usePd) : this(gd, device)
|
public PipelineLayoutCacheEntry(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
Device device,
|
||||||
|
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
||||||
|
bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
|
||||||
{
|
{
|
||||||
DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, usePd, out var pipelineLayout);
|
(DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
||||||
PipelineLayout = pipelineLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, ShaderSource[] shaders) : this(gd, device)
|
|
||||||
{
|
|
||||||
DescriptorSetLayouts = PipelineLayoutFactory.CreateMinimal(gd, device, shaders, out var pipelineLayout);
|
|
||||||
PipelineLayout = pipelineLayout;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
|
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
|
||||||
@@ -58,7 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_dsLastCbIndex = commandBufferIndex;
|
_dsLastCbIndex = commandBufferIndex;
|
||||||
|
|
||||||
for (int i = 0; i < PipelineBase.DescriptorSetLayouts; i++)
|
for (int i = 0; i < _dsCacheCursor.Length; i++)
|
||||||
{
|
{
|
||||||
_dsCacheCursor[i] = 0;
|
_dsCacheCursor[i] = 0;
|
||||||
}
|
}
|
||||||
|
@@ -1,257 +1,74 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System.Collections.Generic;
|
using System.Collections.ObjectModel;
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
static class PipelineLayoutFactory
|
static class PipelineLayoutFactory
|
||||||
{
|
{
|
||||||
private const ShaderStageFlags SupportBufferStages =
|
public static unsafe (DescriptorSetLayout[], PipelineLayout) Create(
|
||||||
ShaderStageFlags.VertexBit |
|
VulkanRenderer gd,
|
||||||
ShaderStageFlags.FragmentBit |
|
Device device,
|
||||||
ShaderStageFlags.ComputeBit;
|
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
||||||
|
bool usePushDescriptors)
|
||||||
private static ShaderStageFlags ActiveStages(uint stages)
|
|
||||||
{
|
{
|
||||||
ShaderStageFlags stageFlags = 0;
|
DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count];
|
||||||
|
|
||||||
while (stages != 0)
|
bool isMoltenVk = gd.IsMoltenVk;
|
||||||
|
|
||||||
|
for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
|
||||||
{
|
{
|
||||||
int stage = BitOperations.TrailingZeroCount(stages);
|
ResourceDescriptorCollection rdc = setDescriptors[setIndex];
|
||||||
stages &= ~(1u << stage);
|
|
||||||
|
|
||||||
stageFlags |= stage switch
|
ResourceStages activeStages = ResourceStages.None;
|
||||||
|
|
||||||
|
if (isMoltenVk)
|
||||||
{
|
{
|
||||||
1 => ShaderStageFlags.FragmentBit,
|
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
|
||||||
2 => ShaderStageFlags.GeometryBit,
|
|
||||||
3 => ShaderStageFlags.TessellationControlBit,
|
|
||||||
4 => ShaderStageFlags.TessellationEvaluationBit,
|
|
||||||
_ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return stageFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
|
|
||||||
{
|
|
||||||
int stagesCount = BitOperations.PopCount(stages);
|
|
||||||
|
|
||||||
int uCount = Constants.MaxUniformBuffersPerStage * stagesCount + 1;
|
|
||||||
int tCount = Constants.MaxTexturesPerStage * 2 * stagesCount;
|
|
||||||
int iCount = Constants.MaxImagesPerStage * 2 * stagesCount;
|
|
||||||
|
|
||||||
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
|
|
||||||
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount];
|
|
||||||
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
|
|
||||||
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
|
|
||||||
|
|
||||||
uLayoutBindings[0] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = 0,
|
|
||||||
DescriptorType = DescriptorType.UniformBuffer,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = SupportBufferStages
|
|
||||||
};
|
|
||||||
|
|
||||||
int iter = 0;
|
|
||||||
var activeStages = ActiveStages(stages);
|
|
||||||
|
|
||||||
while (stages != 0)
|
|
||||||
{
|
|
||||||
int stage = BitOperations.TrailingZeroCount(stages);
|
|
||||||
stages &= ~(1u << stage);
|
|
||||||
|
|
||||||
var stageFlags = stage switch
|
|
||||||
{
|
|
||||||
1 => ShaderStageFlags.FragmentBit,
|
|
||||||
2 => ShaderStageFlags.GeometryBit,
|
|
||||||
3 => ShaderStageFlags.TessellationControlBit,
|
|
||||||
4 => ShaderStageFlags.TessellationEvaluationBit,
|
|
||||||
_ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
|
|
||||||
};
|
|
||||||
|
|
||||||
void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip)
|
|
||||||
{
|
|
||||||
int totalPerStage = maxPerStage * skip;
|
|
||||||
|
|
||||||
for (int i = 0; i < maxPerStage; i++)
|
|
||||||
{
|
{
|
||||||
bindings[start + iter * totalPerStage + i] = new DescriptorSetLayoutBinding
|
activeStages |= rdc.Descriptors[descIndex].Stages;
|
||||||
{
|
|
||||||
Binding = (uint)(start + stage * totalPerStage + i),
|
|
||||||
DescriptorType = type,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = stageFlags
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0)
|
DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count];
|
||||||
{
|
|
||||||
// There's a bug on MoltenVK where using the same buffer across different stages
|
|
||||||
// causes invalid resource errors, allow the binding on all active stages as workaround.
|
|
||||||
var flags = gd.IsMoltenVk ? activeStages : stageFlags;
|
|
||||||
|
|
||||||
bindings[start + iter] = new DescriptorSetLayoutBinding
|
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
|
||||||
|
{
|
||||||
|
ResourceDescriptor descriptor = rdc.Descriptors[descIndex];
|
||||||
|
|
||||||
|
ResourceStages stages = descriptor.Stages;
|
||||||
|
|
||||||
|
if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk)
|
||||||
{
|
{
|
||||||
Binding = (uint)(start + stage * maxPerStage),
|
// There's a bug on MoltenVK where using the same buffer across different stages
|
||||||
DescriptorType = DescriptorType.StorageBuffer,
|
// causes invalid resource errors, allow the binding on all active stages as workaround.
|
||||||
DescriptorCount = (uint)maxPerStage,
|
stages = activeStages;
|
||||||
StageFlags = flags
|
}
|
||||||
|
|
||||||
|
layoutBindings[descIndex] = new DescriptorSetLayoutBinding()
|
||||||
|
{
|
||||||
|
Binding = (uint)descriptor.Binding,
|
||||||
|
DescriptorType = descriptor.Type.Convert(),
|
||||||
|
DescriptorCount = (uint)descriptor.Count,
|
||||||
|
StageFlags = stages.Convert()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Set(uLayoutBindings, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer, 1, 1);
|
fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings)
|
||||||
SetStorage(sLayoutBindings, Constants.MaxStorageBuffersPerStage);
|
|
||||||
Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.CombinedImageSampler, 0, 2);
|
|
||||||
Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.UniformTexelBuffer, Constants.MaxTexturesPerStage, 2);
|
|
||||||
Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageImage, 0, 2);
|
|
||||||
Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageTexelBuffer, Constants.MaxImagesPerStage, 2);
|
|
||||||
|
|
||||||
iter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
|
|
||||||
|
|
||||||
var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = uLayoutBindings,
|
|
||||||
BindingCount = (uint)uCount,
|
|
||||||
Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0
|
|
||||||
};
|
|
||||||
|
|
||||||
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = sLayoutBindings,
|
|
||||||
BindingCount = (uint)stagesCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = tLayoutBindings,
|
|
||||||
BindingCount = (uint)tCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = iLayoutBindings,
|
|
||||||
BindingCount = (uint)iCount
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
|
|
||||||
|
|
||||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
|
||||||
{
|
|
||||||
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
|
|
||||||
{
|
{
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||||
PSetLayouts = pLayouts,
|
|
||||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return layouts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanRenderer gd, Device device, ShaderSource[] shaders, out PipelineLayout layout)
|
|
||||||
{
|
|
||||||
int stagesCount = shaders.Length;
|
|
||||||
|
|
||||||
int uCount = 0;
|
|
||||||
int sCount = 0;
|
|
||||||
int tCount = 0;
|
|
||||||
int iCount = 0;
|
|
||||||
|
|
||||||
foreach (var shader in shaders)
|
|
||||||
{
|
|
||||||
uCount += shader.Bindings.UniformBufferBindings.Count;
|
|
||||||
sCount += shader.Bindings.StorageBufferBindings.Count;
|
|
||||||
tCount += shader.Bindings.TextureBindings.Count;
|
|
||||||
iCount += shader.Bindings.ImageBindings.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
|
|
||||||
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount];
|
|
||||||
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
|
|
||||||
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
|
|
||||||
|
|
||||||
int uIndex = 0;
|
|
||||||
int sIndex = 0;
|
|
||||||
int tIndex = 0;
|
|
||||||
int iIndex = 0;
|
|
||||||
|
|
||||||
foreach (var shader in shaders)
|
|
||||||
{
|
|
||||||
var stageFlags = shader.Stage.Convert();
|
|
||||||
|
|
||||||
void Set(DescriptorSetLayoutBinding* bindings, DescriptorType type, ref int start, IEnumerable<int> bds)
|
|
||||||
{
|
|
||||||
foreach (var b in bds)
|
|
||||||
{
|
{
|
||||||
bindings[start++] = new DescriptorSetLayoutBinding
|
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||||
{
|
PBindings = pLayoutBindings,
|
||||||
Binding = (uint)b,
|
BindingCount = (uint)layoutBindings.Length,
|
||||||
DescriptorType = type,
|
Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None
|
||||||
DescriptorCount = 1,
|
};
|
||||||
StageFlags = stageFlags
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Support buffer textures and images here.
|
gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
|
||||||
// This is only used for the helper shaders on the backend, and we don't use buffer textures on them
|
}
|
||||||
// so far, so it's not really necessary right now.
|
|
||||||
Set(uLayoutBindings, DescriptorType.UniformBuffer, ref uIndex, shader.Bindings.UniformBufferBindings);
|
|
||||||
Set(sLayoutBindings, DescriptorType.StorageBuffer, ref sIndex, shader.Bindings.StorageBufferBindings);
|
|
||||||
Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings);
|
|
||||||
Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
|
PipelineLayout layout;
|
||||||
|
|
||||||
var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = uLayoutBindings,
|
|
||||||
BindingCount = (uint)uCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = sLayoutBindings,
|
|
||||||
BindingCount = (uint)sCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = tLayoutBindings,
|
|
||||||
BindingCount = (uint)tCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = iLayoutBindings,
|
|
||||||
BindingCount = (uint)iCount
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
|
|
||||||
|
|
||||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
fixed (DescriptorSetLayout* pLayouts = layouts)
|
||||||
{
|
{
|
||||||
@@ -259,13 +76,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
SType = StructureType.PipelineLayoutCreateInfo,
|
||||||
PSetLayouts = pLayouts,
|
PSetLayouts = pLayouts,
|
||||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
SetLayoutCount = (uint)layouts.Length
|
||||||
};
|
};
|
||||||
|
|
||||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
||||||
}
|
}
|
||||||
|
|
||||||
return layouts;
|
return (layouts, layout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Silk.NET.Vulkan;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
@@ -308,6 +309,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public PipelineLayout PipelineLayout;
|
public PipelineLayout PipelineLayout;
|
||||||
public SpecData SpecializationData;
|
public SpecData SpecializationData;
|
||||||
|
|
||||||
|
private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2;
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
|
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
|
||||||
@@ -400,7 +403,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
Pipeline pipelineHandle = default;
|
Pipeline pipelineHandle = default;
|
||||||
|
|
||||||
|
bool isMoltenVk = gd.IsMoltenVk;
|
||||||
|
|
||||||
|
if (isMoltenVk)
|
||||||
|
{
|
||||||
|
UpdateVertexAttributeDescriptions();
|
||||||
|
}
|
||||||
|
|
||||||
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
|
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
|
||||||
|
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0])
|
||||||
fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
|
fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
|
||||||
fixed (Viewport* pViewports = &Internal.Viewports[0])
|
fixed (Viewport* pViewports = &Internal.Viewports[0])
|
||||||
fixed (Rect2D* pScissors = &Internal.Scissors[0])
|
fixed (Rect2D* pScissors = &Internal.Scissors[0])
|
||||||
@@ -410,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.PipelineVertexInputStateCreateInfo,
|
SType = StructureType.PipelineVertexInputStateCreateInfo,
|
||||||
VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
|
VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
|
||||||
PVertexAttributeDescriptions = pVertexAttributeDescriptions,
|
PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions,
|
||||||
VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
|
VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
|
||||||
PVertexBindingDescriptions = pVertexBindingDescriptions
|
PVertexBindingDescriptions = pVertexBindingDescriptions
|
||||||
};
|
};
|
||||||
@@ -612,6 +623,40 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateVertexAttributeDescriptions()
|
||||||
|
{
|
||||||
|
// Vertex attributes exceeding the stride are invalid.
|
||||||
|
// In metal, they cause glitches with the vertex shader fetching incorrect values.
|
||||||
|
// To work around this, we reduce the format to something that doesn't exceed the stride if possible.
|
||||||
|
// The assumption is that the exceeding components are not actually accessed on the shader.
|
||||||
|
|
||||||
|
for (int index = 0; index < VertexAttributeDescriptionsCount; index++)
|
||||||
|
{
|
||||||
|
var attribute = Internal.VertexAttributeDescriptions[index];
|
||||||
|
ref var vb = ref Internal.VertexBindingDescriptions[(int)attribute.Binding];
|
||||||
|
|
||||||
|
Format format = attribute.Format;
|
||||||
|
|
||||||
|
while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride)
|
||||||
|
{
|
||||||
|
Format newFormat = FormatTable.DropLastComponent(format);
|
||||||
|
|
||||||
|
if (newFormat == format)
|
||||||
|
{
|
||||||
|
// That case means we failed to find a format that fits within the stride,
|
||||||
|
// so just restore the original format and give up.
|
||||||
|
format = attribute.Format;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = newFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
attribute.Format = format;
|
||||||
|
_vertexAttributeDescriptions2[index] = attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Stages.Dispose();
|
Stages.Dispose();
|
||||||
|
22
src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs
Normal file
22
src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
readonly struct ResourceBindingSegment
|
||||||
|
{
|
||||||
|
public readonly int Binding;
|
||||||
|
public readonly int Count;
|
||||||
|
public readonly ResourceType Type;
|
||||||
|
public readonly ResourceStages Stages;
|
||||||
|
public readonly ResourceAccess Access;
|
||||||
|
|
||||||
|
public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, ResourceAccess access)
|
||||||
|
{
|
||||||
|
Binding = binding;
|
||||||
|
Count = count;
|
||||||
|
Type = type;
|
||||||
|
Stages = stages;
|
||||||
|
Access = access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
Normal file
67
src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
class ResourceLayoutBuilder
|
||||||
|
{
|
||||||
|
private const int TotalSets = PipelineBase.DescriptorSetLayouts;
|
||||||
|
|
||||||
|
private readonly List<ResourceDescriptor>[] _resourceDescriptors;
|
||||||
|
private readonly List<ResourceUsage>[] _resourceUsages;
|
||||||
|
|
||||||
|
public ResourceLayoutBuilder()
|
||||||
|
{
|
||||||
|
_resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
|
||||||
|
_resourceUsages = new List<ResourceUsage>[TotalSets];
|
||||||
|
|
||||||
|
for (int index = 0; index < TotalSets; index++)
|
||||||
|
{
|
||||||
|
_resourceDescriptors[index] = new();
|
||||||
|
_resourceUsages[index] = new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
|
||||||
|
{
|
||||||
|
int setIndex = type switch
|
||||||
|
{
|
||||||
|
ResourceType.UniformBuffer => PipelineBase.UniformSetIndex,
|
||||||
|
ResourceType.StorageBuffer => PipelineBase.StorageSetIndex,
|
||||||
|
ResourceType.TextureAndSampler or ResourceType.BufferTexture => PipelineBase.TextureSetIndex,
|
||||||
|
ResourceType.Image or ResourceType.BufferImage => PipelineBase.ImageSetIndex,
|
||||||
|
_ => throw new ArgumentException($"Invalid resource type \"{type}\".")
|
||||||
|
};
|
||||||
|
|
||||||
|
ResourceAccess access = IsReadOnlyType(type) ? ResourceAccess.Read : ResourceAccess.ReadWrite;
|
||||||
|
|
||||||
|
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
|
||||||
|
_resourceUsages[setIndex].Add(new ResourceUsage(binding, type, stages, access));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsReadOnlyType(ResourceType type)
|
||||||
|
{
|
||||||
|
return type == ResourceType.UniformBuffer ||
|
||||||
|
type == ResourceType.Sampler ||
|
||||||
|
type == ResourceType.TextureAndSampler ||
|
||||||
|
type == ResourceType.BufferTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLayout Build()
|
||||||
|
{
|
||||||
|
var descriptors = new ResourceDescriptorCollection[TotalSets];
|
||||||
|
var usages = new ResourceUsageCollection[TotalSets];
|
||||||
|
|
||||||
|
for (int index = 0; index < TotalSets; index++)
|
||||||
|
{
|
||||||
|
descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
|
||||||
|
usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -26,8 +26,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public ShaderStageFlags StageFlags => _stage;
|
public ShaderStageFlags StageFlags => _stage;
|
||||||
|
|
||||||
public ShaderBindings Bindings { get; }
|
|
||||||
|
|
||||||
public ProgramLinkStatus CompileStatus { private set; get; }
|
public ProgramLinkStatus CompileStatus { private set; get; }
|
||||||
|
|
||||||
public readonly Task CompileTask;
|
public readonly Task CompileTask;
|
||||||
@@ -36,7 +34,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
Bindings = shaderSource.Bindings;
|
|
||||||
|
|
||||||
CompileStatus = ProgramLinkStatus.Incomplete;
|
CompileStatus = ProgramLinkStatus.Incomplete;
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
|
|||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public uint Stages { get; }
|
public uint Stages { get; }
|
||||||
|
|
||||||
public int[][][] Bindings { get; }
|
public ResourceBindingSegment[][] BindingSegments { get; }
|
||||||
|
|
||||||
public ProgramLinkStatus LinkStatus { get; private set; }
|
public ProgramLinkStatus LinkStatus { get; private set; }
|
||||||
|
|
||||||
@@ -54,7 +55,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private Task _compileTask;
|
private Task _compileTask;
|
||||||
private bool _firstBackgroundUse;
|
private bool _firstBackgroundUse;
|
||||||
|
|
||||||
public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, SpecDescription[] specDescription = null, bool isMinimal = false)
|
public ShaderCollection(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
Device device,
|
||||||
|
ShaderSource[] shaders,
|
||||||
|
ResourceLayout resourceLayout,
|
||||||
|
SpecDescription[] specDescription = null,
|
||||||
|
bool isMinimal = false)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
_device = device;
|
_device = device;
|
||||||
@@ -99,39 +106,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_shaders = internalShaders;
|
_shaders = internalShaders;
|
||||||
|
|
||||||
bool usePd = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
|
bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
|
||||||
|
|
||||||
_plce = isMinimal
|
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors);
|
||||||
? gd.PipelineLayoutCache.Create(gd, device, shaders)
|
|
||||||
: gd.PipelineLayoutCache.GetOrCreate(gd, device, stages, usePd);
|
|
||||||
|
|
||||||
HasMinimalLayout = isMinimal;
|
HasMinimalLayout = isMinimal;
|
||||||
UsePushDescriptors = usePd;
|
UsePushDescriptors = usePushDescriptors;
|
||||||
|
|
||||||
Stages = stages;
|
Stages = stages;
|
||||||
|
|
||||||
int[][] GrabAll(Func<ShaderBindings, IReadOnlyCollection<int>> selector)
|
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
|
||||||
{
|
|
||||||
bool hasAny = false;
|
|
||||||
int[][] bindings = new int[internalShaders.Length][];
|
|
||||||
|
|
||||||
for (int i = 0; i < internalShaders.Length; i++)
|
|
||||||
{
|
|
||||||
var collection = selector(internalShaders[i].Bindings);
|
|
||||||
hasAny |= collection.Count != 0;
|
|
||||||
bindings[i] = collection.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasAny ? bindings : Array.Empty<int[]>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Bindings = new[]
|
|
||||||
{
|
|
||||||
GrabAll(x => x.UniformBufferBindings),
|
|
||||||
GrabAll(x => x.StorageBufferBindings),
|
|
||||||
GrabAll(x => x.TextureBindings),
|
|
||||||
GrabAll(x => x.ImageBindings)
|
|
||||||
};
|
|
||||||
|
|
||||||
_compileTask = Task.CompletedTask;
|
_compileTask = Task.CompletedTask;
|
||||||
_firstBackgroundUse = false;
|
_firstBackgroundUse = false;
|
||||||
@@ -141,8 +125,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
ShaderSource[] sources,
|
ShaderSource[] sources,
|
||||||
|
ResourceLayout resourceLayout,
|
||||||
ProgramPipelineState state,
|
ProgramPipelineState state,
|
||||||
bool fromCache) : this(gd, device, sources)
|
bool fromCache) : this(gd, device, sources, resourceLayout)
|
||||||
{
|
{
|
||||||
_state = state;
|
_state = state;
|
||||||
|
|
||||||
@@ -150,6 +135,67 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_firstBackgroundUse = !fromCache;
|
_firstBackgroundUse = !fromCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
||||||
|
{
|
||||||
|
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
|
||||||
|
|
||||||
|
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
|
||||||
|
{
|
||||||
|
List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>();
|
||||||
|
|
||||||
|
ResourceUsage currentUsage = default;
|
||||||
|
int currentCount = 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
|
||||||
|
{
|
||||||
|
ResourceUsage usage = setUsages[setIndex].Usages[index];
|
||||||
|
|
||||||
|
// If the resource is not accessed, we don't need to update it.
|
||||||
|
if (usage.Access == ResourceAccess.None)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUsage.Binding + currentCount != usage.Binding ||
|
||||||
|
currentUsage.Type != usage.Type ||
|
||||||
|
currentUsage.Stages != usage.Stages ||
|
||||||
|
currentUsage.Access != usage.Access)
|
||||||
|
{
|
||||||
|
if (currentCount != 0)
|
||||||
|
{
|
||||||
|
currentSegments.Add(new ResourceBindingSegment(
|
||||||
|
currentUsage.Binding,
|
||||||
|
currentCount,
|
||||||
|
currentUsage.Type,
|
||||||
|
currentUsage.Stages,
|
||||||
|
currentUsage.Access));
|
||||||
|
}
|
||||||
|
|
||||||
|
currentUsage = usage;
|
||||||
|
currentCount = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentCount != 0)
|
||||||
|
{
|
||||||
|
currentSegments.Add(new ResourceBindingSegment(
|
||||||
|
currentUsage.Binding,
|
||||||
|
currentCount,
|
||||||
|
currentUsage.Type,
|
||||||
|
currentUsage.Stages,
|
||||||
|
currentUsage.Access));
|
||||||
|
}
|
||||||
|
|
||||||
|
segments[setIndex] = currentSegments.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task BackgroundCompilation()
|
private async Task BackgroundCompilation()
|
||||||
{
|
{
|
||||||
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
|
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
|
||||||
|
@@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
||||||
|
|
||||||
var usage = GetImageUsageFromFormat(info.Format);
|
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
|
||||||
|
|
||||||
var flags = ImageCreateFlags.CreateMutableFormatBit;
|
var flags = ImageCreateFlags.CreateMutableFormatBit;
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageUsageFlags GetImageUsageFromFormat(GAL.Format format)
|
public static ImageUsageFlags GetImageUsage(GAL.Format format, Target target, bool supportsMsStorage)
|
||||||
{
|
{
|
||||||
var usage = DefaultUsageFlags;
|
var usage = DefaultUsageFlags;
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
usage |= ImageUsageFlags.ColorAttachmentBit;
|
usage |= ImageUsageFlags.ColorAttachmentBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format.IsImageCompatible())
|
if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
|
||||||
{
|
{
|
||||||
usage |= ImageUsageFlags.StorageBit;
|
usage |= ImageUsageFlags.StorageBit;
|
||||||
}
|
}
|
||||||
|
@@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd.Textures.Add(this);
|
gd.Textures.Add(this);
|
||||||
|
|
||||||
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
|
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
|
||||||
var usage = TextureStorage.GetImageUsageFromFormat(info.Format);
|
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
|
||||||
var levels = (uint)info.Levels;
|
var levels = (uint)info.Levels;
|
||||||
var layers = (uint)info.GetLayers();
|
var layers = (uint)info.GetLayers();
|
||||||
|
|
||||||
|
@@ -10,7 +10,6 @@ using Silk.NET.Vulkan.Extensions.EXT;
|
|||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
@@ -296,6 +295,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportsSubgroupSizeControl,
|
supportsSubgroupSizeControl,
|
||||||
featuresShaderInt8.ShaderInt8,
|
featuresShaderInt8.ShaderInt8,
|
||||||
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
|
||||||
|
features2.Features.ShaderStorageImageMultisample,
|
||||||
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
|
||||||
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
|
||||||
features2.Features.MultiViewport,
|
features2.Features.MultiViewport,
|
||||||
@@ -398,17 +398,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (info.State.HasValue || isCompute)
|
if (info.State.HasValue || isCompute)
|
||||||
{
|
{
|
||||||
return new ShaderCollection(this, _device, sources, info.State ?? default, info.FromCache);
|
return new ShaderCollection(this, _device, sources, info.ResourceLayout, info.State ?? default, info.FromCache);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new ShaderCollection(this, _device, sources);
|
return new ShaderCollection(this, _device, sources, info.ResourceLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, SpecDescription[] specDescription = null)
|
internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, ResourceLayout resourceLayout, SpecDescription[] specDescription = null)
|
||||||
{
|
{
|
||||||
return new ShaderCollection(this, _device, sources, specDescription: specDescription, isMinimal: true);
|
return new ShaderCollection(this, _device, sources, resourceLayout, specDescription, isMinimal: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISampler CreateSampler(GAL.SamplerCreateInfo info)
|
public ISampler CreateSampler(GAL.SamplerCreateInfo info)
|
||||||
@@ -600,6 +600,25 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return new HardwareInfo(GpuVendor, GpuRenderer);
|
return new HardwareInfo(GpuVendor, GpuRenderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the available Vulkan devices using the default Vulkan API
|
||||||
|
/// object returned by <see cref="Vk.GetApi()"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static DeviceInfo[] GetPhysicalDevices()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return VulkanInitialization.GetSuitablePhysicalDevices(Vk.GetApi());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Gpu, $"Error querying Vulkan devices: {ex.Message}");
|
||||||
|
|
||||||
|
return Array.Empty<DeviceInfo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static DeviceInfo[] GetPhysicalDevices(Vk api)
|
public static DeviceInfo[] GetPhysicalDevices(Vk api)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -658,7 +677,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
||||||
}
|
}
|
||||||
|
|
||||||
public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
|
internal GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
|
||||||
{
|
{
|
||||||
return topology switch
|
return topology switch
|
||||||
{
|
{
|
||||||
@@ -669,7 +688,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TopologyUnsupported(GAL.PrimitiveTopology topology)
|
internal bool TopologyUnsupported(GAL.PrimitiveTopology topology)
|
||||||
{
|
{
|
||||||
return topology switch
|
return topology switch
|
||||||
{
|
{
|
||||||
|
@@ -167,12 +167,12 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
if (StrEquals(RomfsDir, modDir.Name))
|
if (StrEquals(RomfsDir, modDir.Name))
|
||||||
{
|
{
|
||||||
mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} RomFs>", modDir));
|
mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir));
|
||||||
types.Append('R');
|
types.Append('R');
|
||||||
}
|
}
|
||||||
else if (StrEquals(ExefsDir, modDir.Name))
|
else if (StrEquals(ExefsDir, modDir.Name))
|
||||||
{
|
{
|
||||||
mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} ExeFs>", modDir));
|
mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir));
|
||||||
types.Append('E');
|
types.Append('E');
|
||||||
}
|
}
|
||||||
else if (StrEquals(CheatDir, modDir.Name))
|
else if (StrEquals(CheatDir, modDir.Name))
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
@@ -32,13 +33,14 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
0x01007FFF
|
0x01007FFF
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly object _handleLock = new();
|
// The amount of time Dispose() will wait to Join() the thread executing the ServerLoop()
|
||||||
|
private static readonly TimeSpan ThreadJoinTimeout = TimeSpan.FromSeconds(3);
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
private KProcess _selfProcess;
|
private KProcess _selfProcess;
|
||||||
|
private KThread _selfThread;
|
||||||
|
|
||||||
private readonly List<int> _sessionHandles = new List<int>();
|
private readonly ReaderWriterLockSlim _handleLock = new ReaderWriterLockSlim();
|
||||||
private readonly List<int> _portHandles = new List<int>();
|
|
||||||
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
|
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
|
||||||
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
|
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
|
||||||
|
|
||||||
@@ -48,6 +50,8 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
private readonly MemoryStream _responseDataStream;
|
private readonly MemoryStream _responseDataStream;
|
||||||
private readonly BinaryWriter _responseDataWriter;
|
private readonly BinaryWriter _responseDataWriter;
|
||||||
|
|
||||||
|
private int _isDisposed = 0;
|
||||||
|
|
||||||
public ManualResetEvent InitDone { get; }
|
public ManualResetEvent InitDone { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public Func<IpcService> SmObjectFactory { get; }
|
public Func<IpcService> SmObjectFactory { get; }
|
||||||
@@ -79,11 +83,20 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
||||||
{
|
{
|
||||||
lock (_handleLock)
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_portHandles.Add(serverPortHandle);
|
lockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
_ports.Add(serverPortHandle, objectFactory);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken)
|
||||||
|
{
|
||||||
|
_handleLock.ExitWriteLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ports.Add(serverPortHandle, objectFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSessionObj(KServerSession serverSession, IpcService obj)
|
public void AddSessionObj(KServerSession serverSession, IpcService obj)
|
||||||
@@ -92,16 +105,62 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
InitDone.WaitOne();
|
InitDone.WaitOne();
|
||||||
|
|
||||||
_selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle);
|
_selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle);
|
||||||
|
|
||||||
AddSessionObj(serverSessionHandle, obj);
|
AddSessionObj(serverSessionHandle, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSessionObj(int serverSessionHandle, IpcService obj)
|
public void AddSessionObj(int serverSessionHandle, IpcService obj)
|
||||||
{
|
{
|
||||||
lock (_handleLock)
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_sessionHandles.Add(serverSessionHandle);
|
lockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
_sessions.Add(serverSessionHandle, obj);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken)
|
||||||
|
{
|
||||||
|
_handleLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IpcService GetSessionObj(int serverSessionHandle)
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lockTaken = _handleLock.TryEnterReadLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
return _sessions[serverSessionHandle];
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken)
|
||||||
|
{
|
||||||
|
_handleLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RemoveSessionObj(int serverSessionHandle, out IpcService obj)
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
return _sessions.Remove(serverSessionHandle, out obj);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockTaken)
|
||||||
|
{
|
||||||
|
_handleLock.ExitWriteLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_sessions.Add(serverSessionHandle, obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Main()
|
private void Main()
|
||||||
@@ -112,6 +171,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
private void ServerLoop()
|
private void ServerLoop()
|
||||||
{
|
{
|
||||||
_selfProcess = KernelStatic.GetCurrentProcess();
|
_selfProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
_selfThread = KernelStatic.GetCurrentThread();
|
||||||
|
|
||||||
if (SmObjectFactory != null)
|
if (SmObjectFactory != null)
|
||||||
{
|
{
|
||||||
@@ -122,8 +182,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
InitDone.Set();
|
InitDone.Set();
|
||||||
|
|
||||||
KThread thread = KernelStatic.GetCurrentThread();
|
ulong messagePtr = _selfThread.TlsAddress;
|
||||||
ulong messagePtr = thread.TlsAddress;
|
|
||||||
_context.Syscall.SetHeapSize(out ulong heapAddr, 0x200000);
|
_context.Syscall.SetHeapSize(out ulong heapAddr, 0x200000);
|
||||||
|
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
|
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
|
||||||
@@ -134,27 +193,39 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int handleCount;
|
|
||||||
int portHandleCount;
|
int portHandleCount;
|
||||||
|
int handleCount;
|
||||||
int[] handles;
|
int[] handles;
|
||||||
|
|
||||||
lock (_handleLock)
|
bool handleLockTaken = false;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
portHandleCount = _portHandles.Count;
|
handleLockTaken = _handleLock.TryEnterReadLock(Timeout.Infinite);
|
||||||
handleCount = portHandleCount + _sessionHandles.Count;
|
|
||||||
|
portHandleCount = _ports.Count;
|
||||||
|
|
||||||
|
handleCount = portHandleCount + _sessions.Count;
|
||||||
|
|
||||||
handles = ArrayPool<int>.Shared.Rent(handleCount);
|
handles = ArrayPool<int>.Shared.Rent(handleCount);
|
||||||
|
|
||||||
_portHandles.CopyTo(handles, 0);
|
_ports.Keys.CopyTo(handles, 0);
|
||||||
_sessionHandles.CopyTo(handles, portHandleCount);
|
|
||||||
|
_sessions.Keys.CopyTo(handles, portHandleCount);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (handleLockTaken)
|
||||||
|
{
|
||||||
|
_handleLock.ExitReadLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We still need a timeout here to allow the service to pick up and listen new sessions...
|
// We still need a timeout here to allow the service to pick up and listen new sessions...
|
||||||
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L);
|
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L);
|
||||||
|
|
||||||
thread.HandlePostSyscall();
|
_selfThread.HandlePostSyscall();
|
||||||
|
|
||||||
if (!thread.Context.Running)
|
if (!_selfThread.Context.Running)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -178,9 +249,20 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
// We got a new connection, accept the session to allow servicing future requests.
|
// We got a new connection, accept the session to allow servicing future requests.
|
||||||
if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == Result.Success)
|
if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == Result.Success)
|
||||||
{
|
{
|
||||||
IpcService obj = _ports[handles[signaledIndex]].Invoke();
|
bool handleWriteLockTaken = false;
|
||||||
|
try
|
||||||
AddSessionObj(serverSessionHandle, obj);
|
{
|
||||||
|
handleWriteLockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite);
|
||||||
|
IpcService obj = _ports[handles[signaledIndex]].Invoke();
|
||||||
|
_sessions.Add(serverSessionHandle, obj);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (handleWriteLockTaken)
|
||||||
|
{
|
||||||
|
_handleLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,11 +279,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
private bool Process(int serverSessionHandle, ulong recvListAddr)
|
private bool Process(int serverSessionHandle, ulong recvListAddr)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
IpcMessage request = ReadRequest();
|
||||||
KThread thread = KernelStatic.GetCurrentThread();
|
|
||||||
ulong messagePtr = thread.TlsAddress;
|
|
||||||
|
|
||||||
IpcMessage request = ReadRequest(process, messagePtr);
|
|
||||||
|
|
||||||
IpcMessage response = new IpcMessage();
|
IpcMessage response = new IpcMessage();
|
||||||
|
|
||||||
@@ -247,15 +325,15 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
ServiceCtx context = new ServiceCtx(
|
||||||
_context.Device,
|
_context.Device,
|
||||||
process,
|
_selfProcess,
|
||||||
process.CpuMemory,
|
_selfProcess.CpuMemory,
|
||||||
thread,
|
_selfThread,
|
||||||
request,
|
request,
|
||||||
response,
|
response,
|
||||||
_requestDataReader,
|
_requestDataReader,
|
||||||
_responseDataWriter);
|
_responseDataWriter);
|
||||||
|
|
||||||
_sessions[serverSessionHandle].CallCmifMethod(context);
|
GetSessionObj(serverSessionHandle).CallCmifMethod(context);
|
||||||
|
|
||||||
response.RawData = _responseDataStream.ToArray();
|
response.RawData = _responseDataStream.ToArray();
|
||||||
}
|
}
|
||||||
@@ -268,7 +346,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
switch (cmdId)
|
switch (cmdId)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
|
FillHipcResponse(response, 0, GetSessionObj(serverSessionHandle).ConvertToDomain());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
@@ -278,17 +356,31 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
||||||
case 2:
|
case 2:
|
||||||
case 4:
|
case 4:
|
||||||
int unknown = _requestDataReader.ReadInt32();
|
{
|
||||||
|
_ = _requestDataReader.ReadInt32();
|
||||||
|
|
||||||
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
|
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
|
||||||
|
|
||||||
AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
|
bool writeLockTaken = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
writeLockTaken = _handleLock.TryEnterWriteLock(Timeout.Infinite);
|
||||||
|
_sessions[dupServerSessionHandle] = _sessions[serverSessionHandle];
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (writeLockTaken)
|
||||||
|
{
|
||||||
|
_handleLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
|
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
|
||||||
|
|
||||||
FillHipcResponse(response, 0);
|
FillHipcResponse(response, 0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: throw new NotImplementedException(cmdId.ToString());
|
default: throw new NotImplementedException(cmdId.ToString());
|
||||||
}
|
}
|
||||||
@@ -296,13 +388,10 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
else if (request.Type == IpcMessageType.CmifCloseSession || request.Type == IpcMessageType.TipcCloseSession)
|
else if (request.Type == IpcMessageType.CmifCloseSession || request.Type == IpcMessageType.TipcCloseSession)
|
||||||
{
|
{
|
||||||
_context.Syscall.CloseHandle(serverSessionHandle);
|
_context.Syscall.CloseHandle(serverSessionHandle);
|
||||||
lock (_handleLock)
|
if (RemoveSessionObj(serverSessionHandle, out var session))
|
||||||
{
|
{
|
||||||
_sessionHandles.Remove(serverSessionHandle);
|
(session as IDisposable)?.Dispose();
|
||||||
}
|
}
|
||||||
IpcService service = _sessions[serverSessionHandle];
|
|
||||||
(service as IDisposable)?.Dispose();
|
|
||||||
_sessions.Remove(serverSessionHandle);
|
|
||||||
shouldReply = false;
|
shouldReply = false;
|
||||||
}
|
}
|
||||||
// If the type is past 0xF, we are using TIPC
|
// If the type is past 0xF, we are using TIPC
|
||||||
@@ -317,20 +406,20 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
ServiceCtx context = new ServiceCtx(
|
||||||
_context.Device,
|
_context.Device,
|
||||||
process,
|
_selfProcess,
|
||||||
process.CpuMemory,
|
_selfProcess.CpuMemory,
|
||||||
thread,
|
_selfThread,
|
||||||
request,
|
request,
|
||||||
response,
|
response,
|
||||||
_requestDataReader,
|
_requestDataReader,
|
||||||
_responseDataWriter);
|
_responseDataWriter);
|
||||||
|
|
||||||
_sessions[serverSessionHandle].CallTipcMethod(context);
|
GetSessionObj(serverSessionHandle).CallTipcMethod(context);
|
||||||
|
|
||||||
response.RawData = _responseDataStream.ToArray();
|
response.RawData = _responseDataStream.ToArray();
|
||||||
|
|
||||||
using var responseStream = response.GetStreamTipc();
|
using var responseStream = response.GetStreamTipc();
|
||||||
process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
|
_selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -339,27 +428,24 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
if (!isTipcCommunication)
|
if (!isTipcCommunication)
|
||||||
{
|
{
|
||||||
using var responseStream = response.GetStream((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48));
|
using var responseStream = response.GetStream((long)_selfThread.TlsAddress, recvListAddr | ((ulong)PointerBufferSize << 48));
|
||||||
process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
|
_selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence());
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldReply;
|
return shouldReply;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IpcMessage ReadRequest(KProcess process, ulong messagePtr)
|
private IpcMessage ReadRequest()
|
||||||
{
|
{
|
||||||
const int messageSize = 0x100;
|
const int messageSize = 0x100;
|
||||||
|
|
||||||
byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize);
|
using IMemoryOwner<byte> reqDataOwner = ByteMemoryPool.Shared.Rent(messageSize);
|
||||||
|
|
||||||
Span<byte> reqDataSpan = reqData.AsSpan(0, messageSize);
|
Span<byte> reqDataSpan = reqDataOwner.Memory.Span;
|
||||||
reqDataSpan.Clear();
|
|
||||||
|
|
||||||
process.CpuMemory.Read(messagePtr, reqDataSpan);
|
_selfProcess.CpuMemory.Read(_selfThread.TlsAddress, reqDataSpan);
|
||||||
|
|
||||||
IpcMessage request = new IpcMessage(reqDataSpan, (long)messagePtr);
|
IpcMessage request = new IpcMessage(reqDataSpan, (long)_selfThread.TlsAddress);
|
||||||
|
|
||||||
ArrayPool<byte>.Shared.Return(reqData);
|
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
@@ -392,26 +478,35 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing && _selfThread != null)
|
||||||
{
|
{
|
||||||
foreach (IpcService service in _sessions.Values)
|
if (_selfThread.HostThread.ManagedThreadId != Environment.CurrentManagedThreadId && _selfThread.HostThread.Join(ThreadJoinTimeout) == false)
|
||||||
{
|
{
|
||||||
if (service is IDisposable disposableObj)
|
Logger.Warning?.Print(LogClass.Service, $"The ServerBase thread didn't terminate within {ThreadJoinTimeout:g}, waiting longer.");
|
||||||
{
|
|
||||||
disposableObj.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
service.DestroyAtExit();
|
_selfThread.HostThread.Join(Timeout.Infinite);
|
||||||
}
|
}
|
||||||
|
|
||||||
_sessions.Clear();
|
if (Interlocked.Exchange(ref _isDisposed, 1) == 0)
|
||||||
|
{
|
||||||
|
foreach (IpcService service in _sessions.Values)
|
||||||
|
{
|
||||||
|
(service as IDisposable)?.Dispose();
|
||||||
|
|
||||||
_requestDataReader.Dispose();
|
service.DestroyAtExit();
|
||||||
_requestDataStream.Dispose();
|
}
|
||||||
_responseDataWriter.Dispose();
|
|
||||||
_responseDataStream.Dispose();
|
|
||||||
|
|
||||||
InitDone.Dispose();
|
_sessions.Clear();
|
||||||
|
_ports.Clear();
|
||||||
|
_handleLock.Dispose();
|
||||||
|
|
||||||
|
_requestDataReader.Dispose();
|
||||||
|
_requestDataStream.Dispose();
|
||||||
|
_responseDataWriter.Dispose();
|
||||||
|
_responseDataStream.Dispose();
|
||||||
|
|
||||||
|
InitDone.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 8.0 KiB |
@@ -478,7 +478,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
if (Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan)
|
if (Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan)
|
||||||
{
|
{
|
||||||
var devices = VulkanRenderer.GetPhysicalDevices(Vk.GetApi());
|
var devices = VulkanRenderer.GetPhysicalDevices();
|
||||||
string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
|
string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
|
||||||
string preferredGpuId = preferredGpuIdFromConfig;
|
string preferredGpuId = preferredGpuIdFromConfig;
|
||||||
bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig);
|
bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig);
|
||||||
|
Reference in New Issue
Block a user