Compare commits

..

4 Commits

Author SHA1 Message Date
riperiperi
4c0eb91d7e Convert Quads to Triangles in Vulkan (#3715)
* Add Index Buffer conversion for quads to Vulkan

Also adds a reusable repeating pattern index buffer to use for non-indexed
draws, and generalizes the conversion cache for buffers.

* Fix some issues

* End render pass before conversion

* Resume transform feedback after we ensure we're in a pass.

* Always generate UInt32 type indices for topology conversion

* No it's not.

* Remove unused code

* Rely on TopologyRemap to convert quads to tris.

* Remove double newline

* Ensure render pass ends before stride or I8 conversion
2022-09-20 18:38:48 -03:00
gdkchan
da75a9a6ea OpenGL: Fix blit from non-multisample to multisample texture (#3596)
* OpenGL: Fix blit from non-multisample to multisample texture

* New approach for multisample copy using compute shaders
2022-09-19 16:12:56 -03:00
MutantAura
41790aa743 Avalonia - Misc changes to UX (#3643)
* Change navbar from compact to default and force text overflow globally

* Fix settings window

* Fix right stick control alignment

* Initialize value and add logging for SDL IDs

* Fix alignment of setting text and improve borders

* Clean up padding and size of buttons on controller settings

* Fix right side trigger alignment and correct styling

* Revert axaml alignment

* Fix alignment of volume widget

* Fix timezone autocompletebox dropdown height

* MainWindow: Line up volume status bar item

* Remove margins and add padding to volume widget

* Make volume text localizable.

Co-authored-by: merry <git@mary.rs>
2022-09-19 16:04:22 -03:00
gdkchan
0cb1e926b5 Allow bindless textures with handles from unbound constant buffer (#3706) 2022-09-19 15:35:47 -03:00
29 changed files with 973 additions and 311 deletions

View File

@@ -879,7 +879,7 @@ namespace Ryujinx.Ava
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
Device.EnableDeviceVsync, Device.EnableDeviceVsync,
Device.GetVolume(), LocaleManager.Instance["VolumeShort"] + $": {(int)(Device.GetVolume() * 100)}%",
Renderer.IsVulkan ? "Vulkan" : "OpenGL", Renderer.IsVulkan ? "Vulkan" : "OpenGL",
dockedMode, dockedMode,
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),

View File

@@ -588,5 +588,6 @@
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.", "SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
"SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied", "SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied",
"SettingsGpuBackendRestartSubMessage": "Do you want to restart now?" "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?",
"VolumeShort": "Vol"
} }

View File

@@ -54,6 +54,12 @@
<Style Selector="Border.huge"> <Style Selector="Border.huge">
<Setter Property="Width" Value="200" /> <Setter Property="Width" Value="200" />
</Style> </Style>
<Style Selector="Border.settings">
<Setter Property="Background" Value="{DynamicResource ThemeDarkColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="CornerRadius" Value="3" />
</Style>
<Style Selector="Image.small"> <Style Selector="Image.small">
<Setter Property="Width" Value="50" /> <Setter Property="Width" Value="50" />
</Style> </Style>
@@ -193,6 +199,14 @@
<Setter Property="Margin" Value="{DynamicResource TextMargin}" /> <Setter Property="Margin" Value="{DynamicResource TextMargin}" />
<Setter Property="FontSize" Value="{DynamicResource FontSize}" /> <Setter Property="FontSize" Value="{DynamicResource FontSize}" />
<Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
</Style>
<Style Selector="TextBlock.h1">
<Setter Property="Margin" Value="{DynamicResource TextMargin}" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="16" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
</Style> </Style>
<Style Selector="Separator"> <Style Selector="Separator">
<Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" /> <Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />

View File

@@ -5,7 +5,7 @@ namespace Ryujinx.Ava.Ui.Models
internal class StatusUpdatedEventArgs : EventArgs internal class StatusUpdatedEventArgs : EventArgs
{ {
public bool VSyncEnabled { get; } public bool VSyncEnabled { get; }
public float Volume { get; } public string VolumeStatus { get; }
public string GpuBackend { get; } public string GpuBackend { get; }
public string AspectRatio { get; } public string AspectRatio { get; }
public string DockedMode { get; } public string DockedMode { get; }
@@ -13,10 +13,10 @@ namespace Ryujinx.Ava.Ui.Models
public string GameStatus { get; } public string GameStatus { get; }
public string GpuName { get; } public string GpuName { get; }
public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) public StatusUpdatedEventArgs(bool vSyncEnabled, string volumeStatus, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName)
{ {
VSyncEnabled = vSyncEnabled; VSyncEnabled = vSyncEnabled;
Volume = volume; VolumeStatus = volumeStatus;
GpuBackend = gpuBackend; GpuBackend = gpuBackend;
DockedMode = dockedMode; DockedMode = dockedMode;
AspectRatio = aspectRatio; AspectRatio = aspectRatio;

View File

@@ -43,6 +43,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
private PlayerIndex _playerId; private PlayerIndex _playerId;
private int _controller; private int _controller;
private int _controllerNumber = 0;
private string _controllerImage; private string _controllerImage;
private int _device; private int _device;
private object _configuration; private object _configuration;
@@ -439,6 +440,14 @@ namespace Ryujinx.Ava.Ui.ViewModels
return str; return str;
} }
private static string GetShortGamepadId(string str)
{
const string Hyphen = "-";
const int Offset = 1;
return str.Substring(str.IndexOf(Hyphen) + Offset);
}
public void LoadDevices() public void LoadDevices()
{ {
lock (Devices) lock (Devices)
@@ -451,9 +460,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id); using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}");
if (gamepad != null) if (gamepad != null)
{ {
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)} ({id})")); Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
} }
} }
@@ -461,12 +472,21 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id); using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}");
if (gamepad != null) if (gamepad != null)
{ {
Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({id})")); if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
{
_controllerNumber++;
}
Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({_controllerNumber})"));
} }
} }
_controllerNumber = 0;
DeviceList.AddRange(Devices.Select(x => x.Name)); DeviceList.AddRange(Devices.Select(x => x.Name));
Device = Math.Min(Device, DeviceList.Count); Device = Math.Min(Device, DeviceList.Count);
} }

View File

@@ -50,6 +50,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
private string _dockedStatusText; private string _dockedStatusText;
private string _fifoStatusText; private string _fifoStatusText;
private string _gameStatusText; private string _gameStatusText;
private string _volumeStatusText;
private string _gpuStatusText; private string _gpuStatusText;
private bool _isAmiiboRequested; private bool _isAmiiboRequested;
private bool _isGameRunning; private bool _isGameRunning;
@@ -385,11 +386,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public string VolumeStatusText public string VolumeStatusText
{ {
get get => _volumeStatusText;
set
{ {
string icon = Volume == 0 ? "🔇" : "🔊"; _volumeStatusText = value;
return $"{icon} {(int)(Volume * 100)}%"; OnPropertyChanged();
} }
} }

View File

@@ -35,7 +35,8 @@
Grid.Column="0" Grid.Column="0"
Margin="0,0,2,0" Margin="0,0,2,0"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"> BorderThickness="1"
Padding="2,0">
<StackPanel <StackPanel
Margin="2" Margin="2"
HorizontalAlignment="Center" HorizontalAlignment="Center"
@@ -65,7 +66,8 @@
Grid.Column="1" Grid.Column="1"
Margin="0,0,2,0" Margin="0,0,2,0"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"> BorderThickness="1"
Padding="2,0">
<StackPanel <StackPanel
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -103,7 +105,8 @@
Grid.Column="2" Grid.Column="2"
Margin="0,0,2,0" Margin="0,0,2,0"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"> BorderThickness="1"
Padding="2,0">
<Grid <Grid
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -132,7 +135,8 @@
<Border <Border
Grid.Column="3" Grid.Column="3"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"> BorderThickness="1"
Padding="2,0" >
<StackPanel <StackPanel
Margin="2" Margin="2"
HorizontalAlignment="Center" HorizontalAlignment="Center"
@@ -151,7 +155,7 @@
Items="{Binding ProfilesList}" Items="{Binding ProfilesList}"
Text="{Binding ProfileName}" /> Text="{Binding ProfileName}" />
<Button <Button
MinWidth="60" MinWidth="0"
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ControllerSettingsLoadProfileToolTip}" ToolTip.Tip="{locale:Locale ControllerSettingsLoadProfileToolTip}"
@@ -162,7 +166,7 @@
Height="20" /> Height="20" />
</Button> </Button>
<Button <Button
MinWidth="60" MinWidth="0"
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}" ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}"
@@ -173,7 +177,7 @@
Height="20" /> Height="20" />
</Button> </Button>
<Button <Button
MinWidth="60" MinWidth="0"
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ControllerSettingsRemoveProfileToolTip}" ToolTip.Tip="{locale:Locale ControllerSettingsRemoveProfileToolTip}"
@@ -522,7 +526,7 @@
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<!-- Left DPad Down --> <!-- Left DPad Down -->
<StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal"> <StackPanel Margin="0,0,0,4" Background="{DynamicResource ThemeDarkColor}" Orientation="Horizontal">
<TextBlock <TextBlock
@@ -583,7 +587,7 @@
</StackPanel> </StackPanel>
</Border> </Border>
</Grid> </Grid>
<!-- Triggers And Side Buttons--> <!-- Triggers And Side Buttons-->
<StackPanel Grid.Column="1" HorizontalAlignment="Stretch"> <StackPanel Grid.Column="1" HorizontalAlignment="Stretch">
<Border <Border
@@ -717,7 +721,7 @@
MinWidth="0" MinWidth="0"
Grid.Column="0" Grid.Column="0"
IsChecked="{Binding Configuration.EnableMotion, Mode=TwoWay}"> IsChecked="{Binding Configuration.EnableMotion, Mode=TwoWay}">
<TextBlock Text="{locale:Locale ControllerSettingsMotion}" TextWrapping="WrapWithOverflow" /> <TextBlock Text="{locale:Locale ControllerSettingsMotion}" />
</CheckBox> </CheckBox>
<Button Margin="10" Grid.Column="1" Command="{Binding ShowMotionConfig}"> <Button Margin="10" Grid.Column="1" Command="{Binding ShowMotionConfig}">
<TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" /> <TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" />
@@ -739,7 +743,7 @@
MinWidth="0" MinWidth="0"
Grid.Column="0" Grid.Column="0"
IsChecked="{Binding Configuration.EnableRumble, Mode=TwoWay}"> IsChecked="{Binding Configuration.EnableRumble, Mode=TwoWay}">
<TextBlock Text="{locale:Locale ControllerSettingsRumble}" TextWrapping="WrapWithOverflow" /> <TextBlock Text="{locale:Locale ControllerSettingsRumble}" />
</CheckBox> </CheckBox>
<Button Margin="10" Grid.Column="1" Command="{Binding ShowRumbleConfig}"> <Button Margin="10" Grid.Column="1" Command="{Binding ShowRumbleConfig}">
<TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" /> <TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" />
@@ -780,8 +784,6 @@
Margin="0,0,0,4" Margin="0,0,0,4"
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="{DynamicResource ThemeDarkColor}" Background="{DynamicResource ThemeDarkColor}"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock <TextBlock
@@ -822,7 +824,7 @@
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Margin="0,0,0,4" Margin="0,0,8,4"
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
HorizontalAlignment="Right" HorizontalAlignment="Right"
@@ -957,7 +959,6 @@
</Border> </Border>
<Border <Border
Grid.Row="2" Grid.Row="2"
Margin="2,0,2,2"
Padding="10" Padding="10"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
@@ -1139,7 +1140,7 @@
Text="{Binding Configuration.DeadzoneRight, StringFormat=\{0:0.00\}}" /> Text="{Binding Configuration.DeadzoneRight, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<TextBlock Text="{locale:Locale ControllerSettingsStickRange}" /> <TextBlock Text="{locale:Locale ControllerSettingsStickRange}" />
<StackPanel <StackPanel
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Orientation="Horizontal"> Orientation="Horizontal">

View File

@@ -177,7 +177,7 @@
Command="{ReflectionBinding ChangeLanguage}" Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="zh_TW" CommandParameter="zh_TW"
Header="繁體中文" /> Header="繁體中文" />
<MenuItem <MenuItem
Command="{ReflectionBinding ChangeLanguage}" Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ja_JP" CommandParameter="ja_JP"
Header="日本語" /> Header="日本語" />
@@ -662,12 +662,12 @@
IsVisible="{Binding !ShowLoadProgress}" /> IsVisible="{Binding !ShowLoadProgress}" />
<ui:ToggleSplitButton <ui:ToggleSplitButton
Name="VolumeStatus" Name="VolumeStatus"
Margin="-2,0,-3,0" Padding="5"
Padding="5,0,0,5"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
VerticalContentAlignment="Center"
Background="{DynamicResource ThemeContentBackgroundColor}" Background="{DynamicResource ThemeContentBackgroundColor}"
BorderBrush="{DynamicResource ThemeContentBackgroundColor}" BorderThickness="0"
Content="{Binding VolumeStatusText}" Content="{Binding VolumeStatusText}"
IsChecked="{Binding VolumeMuted}" IsChecked="{Binding VolumeMuted}"
IsVisible="{Binding !ShowLoadProgress}"> IsVisible="{Binding !ShowLoadProgress}">

View File

@@ -138,6 +138,7 @@ namespace Ryujinx.Ava.Ui.Windows
ViewModel.DockedStatusText = args.DockedMode; ViewModel.DockedStatusText = args.DockedMode;
ViewModel.AspectRatioStatusText = args.AspectRatio; ViewModel.AspectRatioStatusText = args.AspectRatio;
ViewModel.GameStatusText = args.GameStatus; ViewModel.GameStatusText = args.GameStatus;
ViewModel.VolumeStatusText = args.VolumeStatus;
ViewModel.FifoStatusText = args.FifoStatus; ViewModel.FifoStatusText = args.FifoStatus;
ViewModel.GpuNameText = args.GpuName; ViewModel.GpuNameText = args.GpuName;
ViewModel.BackendText = args.GpuBackend; ViewModel.BackendText = args.GpuBackend;

View File

@@ -38,18 +38,18 @@
KeyboardNavigation.IsTabStop="False"/> KeyboardNavigation.IsTabStop="False"/>
<Grid Name="Pages" IsVisible="False" Grid.Row="2"> <Grid Name="Pages" IsVisible="False" Grid.Row="2">
<ScrollViewer Name="UiPage" <ScrollViewer Name="UiPage"
Margin="0,0,10,0" Margin="0,0,2,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel <StackPanel
Margin="10,5" Margin="10,5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralGeneral}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGeneral}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical"> <StackPanel Margin="10,0,0,0" Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableDiscordIntegration}"> <CheckBox IsChecked="{Binding EnableDiscordIntegration}">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
@@ -67,7 +67,7 @@
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralGameDirectories}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
<StackPanel <StackPanel
Margin="10,0,0,0" Margin="10,0,0,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -111,7 +111,7 @@
</Grid> </Grid>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGeneralTheme}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralTheme}" />
<Grid Margin="10,0,0,0"> <Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@@ -169,7 +169,7 @@
Padding="0,0,2,0" Padding="0,0,2,0"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel Margin="4" Orientation="Vertical"> <StackPanel Margin="4" Orientation="Vertical">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<CheckBox Margin="5,0" <CheckBox Margin="5,0"
@@ -198,9 +198,9 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel Margin="10,5" Orientation="Vertical" Spacing="10"> <StackPanel Margin="10,5" Orientation="Vertical" Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabHotkeysHotkeys}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal"> <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" /> <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked"> <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
@@ -265,13 +265,13 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel <StackPanel
Margin="10,5" Margin="10,5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabSystemCore}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical"> <StackPanel Margin="10,0,0,0" Orientation="Vertical">
<StackPanel Margin="0,0,0,10" Orientation="Horizontal"> <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
@@ -384,6 +384,7 @@
<AutoCompleteBox <AutoCompleteBox
Name="TimeZoneBox" Name="TimeZoneBox"
Width="350" Width="350"
MaxDropDownHeight="500"
FilterMode="Contains" FilterMode="Contains"
Items="{Binding TimeZones}" Items="{Binding TimeZones}"
SelectionChanged="TimeZoneBox_OnSelectionChanged" SelectionChanged="TimeZoneBox_OnSelectionChanged"
@@ -420,7 +421,7 @@
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabSystemHacks}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
<TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" /> <TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
@@ -445,13 +446,13 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel <StackPanel
Margin="10,5" Margin="10,5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabCpuCache}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuCache}" />
<StackPanel <StackPanel
Margin="10,0,0,0" Margin="10,0,0,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -462,7 +463,7 @@
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabCpuMemory}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuMemory}" />
<StackPanel <StackPanel
Margin="10,0,0,0" Margin="10,0,0,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -502,44 +503,46 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel <StackPanel
Margin="10,5" Margin="10,5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsAPI}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsAPI}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10"> <StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}" ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
Text="{locale:Locale SettingsTabGraphicsBackend}" Text="{locale:Locale SettingsTabGraphicsBackend}"
Width="250" /> Width="250" />
<ComboBox Width="350" <ComboBox Width="350"
HorizontalContentAlignment="Left" HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}" ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
SelectedIndex="{Binding GraphicsBackendIndex}"> SelectedIndex="{Binding GraphicsBackendIndex}">
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}"> <ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
<TextBlock Text="Vulkan" /> <TextBlock Text="Vulkan" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="OpenGL" /> <TextBlock Text="OpenGL" />
</ComboBoxItem> </ComboBoxItem>
</ComboBox> </ComboBox>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" IsVisible="{Binding IsVulkanSelected}"> <StackPanel Orientation="Horizontal" IsVisible="{Binding IsVulkanSelected}">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}" ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
Text="{locale:Locale SettingsTabGraphicsPreferredGpu}" Text="{locale:Locale SettingsTabGraphicsPreferredGpu}"
Width="250" /> Width="250" />
<ComboBox Width="350" <ComboBox Width="350"
HorizontalContentAlignment="Left" HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}" ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
SelectedIndex="{Binding PreferredGpuIndex}" SelectedIndex="{Binding PreferredGpuIndex}"
Items="{Binding AvailableGpus}"/> Items="{Binding AvailableGpus}"/>
</StackPanel> </StackPanel>
<Separator Height="1" /> </StackPanel>
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsFeatures}" /> <Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableShaderCache}" <CheckBox IsChecked="{Binding EnableShaderCache}"
ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}"> ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}">
@@ -673,7 +676,7 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
<StackPanel <StackPanel
Margin="10,0,0,0" Margin="10,0,0,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -698,13 +701,13 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel <StackPanel
Margin="10,5" Margin="10,5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabAudio}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabAudio}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal"> <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemAudioBackend}" Text="{locale:Locale SettingsTabSystemAudioBackend}"
@@ -763,13 +766,13 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel <StackPanel
Margin="10,5" Margin="10,5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabNetworkConnection}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkConnection}" />
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}"> <CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}" <TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" /> ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
@@ -783,13 +786,13 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border> <Border Classes="settings">
<StackPanel <StackPanel
Margin="10,5" Margin="10,5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10">
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabLoggingLogging}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingLogging}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical"> <StackPanel Margin="10,0,0,0" Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableFileLog}" <CheckBox IsChecked="{Binding EnableFileLog}"
ToolTip.Tip="{locale:Locale FileLogTooltip}"> ToolTip.Tip="{locale:Locale FileLogTooltip}">
@@ -821,7 +824,7 @@
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<TextBlock FontWeight="Bold" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
<StackPanel <StackPanel
Margin="10,0,0,0" Margin="10,0,0,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -880,10 +883,15 @@
</Border> </Border>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
<ui:NavigationView Grid.Row="1" IsSettingsVisible="False" Name="NavPanel" IsBackEnabled="False" <ui:NavigationView Grid.Row="1"
PaneDisplayMode="LeftCompact" IsSettingsVisible="False"
Name="NavPanel"
IsBackEnabled="False"
PaneDisplayMode="Left"
Margin="2,10,10,0" Margin="2,10,10,0"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
OpenPaneLength="200">
<ui:NavigationView.MenuItems> <ui:NavigationView.MenuItems>
<ui:NavigationViewItem IsSelected="True" <ui:NavigationViewItem IsSelected="True"
Content="{locale:Locale SettingsTabGeneral}" Content="{locale:Locale SettingsTabGeneral}"

View File

@@ -806,7 +806,9 @@ namespace Ryujinx.Graphics.Gpu.Image
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex) ? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex); : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
int handle = _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4); int handle = textureBufferAddress != 0
? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
: 0;
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader) // The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses // is a 13-bit value. However, in order to also support separate samplers and textures (which uses
@@ -824,7 +826,9 @@ namespace Ryujinx.Graphics.Gpu.Image
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex) ? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex); : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
samplerHandle = _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4); samplerHandle = samplerBufferAddress != 0
? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
: 0;
} }
else else
{ {

View File

@@ -1,98 +0,0 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.OpenGL.Image
{
class IntermmediatePool : IDisposable
{
private readonly OpenGLRenderer _renderer;
private readonly List<TextureView> _entries;
public IntermmediatePool(OpenGLRenderer renderer)
{
_renderer = renderer;
_entries = new List<TextureView>();
}
public TextureView GetOrCreateWithAtLeast(
Target target,
int blockWidth,
int blockHeight,
int bytesPerPixel,
Format format,
int width,
int height,
int depth,
int levels)
{
TextureView entry;
for (int i = 0; i < _entries.Count; i++)
{
entry = _entries[i];
if (entry.Target == target && entry.Format == format)
{
if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels)
{
width = Math.Max(width, entry.Width);
height = Math.Max(height, entry.Height);
depth = Math.Max(depth, entry.Info.Depth);
levels = Math.Max(levels, entry.Info.Levels);
entry.Dispose();
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
_entries[i] = entry;
}
return entry;
}
}
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
_entries.Add(entry);
return entry;
}
private TextureView CreateNew(
Target target,
int blockWidth,
int blockHeight,
int bytesPerPixel,
Format format,
int width,
int height,
int depth,
int levels)
{
return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
width,
height,
depth,
levels,
1,
blockWidth,
blockHeight,
bytesPerPixel,
format,
DepthStencilMode.Depth,
target,
SwizzleComponent.Red,
SwizzleComponent.Green,
SwizzleComponent.Blue,
SwizzleComponent.Alpha), 1f);
}
public void Dispose()
{
foreach (TextureView entry in _entries)
{
entry.Dispose();
}
_entries.Clear();
}
}
}

View File

@@ -9,8 +9,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
private readonly OpenGLRenderer _renderer; private readonly OpenGLRenderer _renderer;
public IntermmediatePool IntermmediatePool { get; }
private int _srcFramebuffer; private int _srcFramebuffer;
private int _dstFramebuffer; private int _dstFramebuffer;
@@ -20,7 +18,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
public TextureCopy(OpenGLRenderer renderer) public TextureCopy(OpenGLRenderer renderer)
{ {
_renderer = renderer; _renderer = renderer;
IntermmediatePool = new IntermmediatePool(renderer);
} }
public void Copy( public void Copy(
@@ -517,8 +514,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
_copyPboHandle = 0; _copyPboHandle = 0;
} }
IntermmediatePool.Dispose();
} }
} }
} }

View File

@@ -0,0 +1,276 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.GAL;
using System;
using System.Numerics;
namespace Ryujinx.Graphics.OpenGL.Image
{
class TextureCopyMS
{
private const string ComputeShaderMSToNonMS = @"#version 450 core
layout (binding = 0, $FORMAT$) uniform uimage2DMS imgIn;
layout (binding = 1, $FORMAT$) uniform uimage2D imgOut;
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
uvec2 coords = gl_GlobalInvocationID.xy;
ivec2 imageSz = imageSize(imgOut);
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
{
return;
}
int inSamples = imageSamples(imgIn);
int samplesInXLog2 = 0;
int samplesInYLog2 = 0;
switch (inSamples)
{
case 2:
samplesInXLog2 = 1;
break;
case 4:
samplesInXLog2 = 1;
samplesInYLog2 = 1;
break;
case 8:
samplesInXLog2 = 2;
samplesInYLog2 = 1;
break;
case 16:
samplesInXLog2 = 2;
samplesInYLog2 = 2;
break;
}
int samplesInX = 1 << samplesInXLog2;
int samplesInY = 1 << samplesInYLog2;
int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
uvec4 value = imageLoad(imgIn, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx);
imageStore(imgOut, ivec2(coords), value);
}";
private const string ComputeShaderNonMSToMS = @"#version 450 core
layout (binding = 0, $FORMAT$) uniform uimage2D imgIn;
layout (binding = 1, $FORMAT$) uniform uimage2DMS imgOut;
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
uvec2 coords = gl_GlobalInvocationID.xy;
ivec2 imageSz = imageSize(imgIn);
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
{
return;
}
int outSamples = imageSamples(imgOut);
int samplesInXLog2 = 0;
int samplesInYLog2 = 0;
switch (outSamples)
{
case 2:
samplesInXLog2 = 1;
break;
case 4:
samplesInXLog2 = 1;
samplesInYLog2 = 1;
break;
case 8:
samplesInXLog2 = 2;
samplesInYLog2 = 1;
break;
case 16:
samplesInXLog2 = 2;
samplesInYLog2 = 2;
break;
}
int samplesInX = 1 << samplesInXLog2;
int samplesInY = 1 << samplesInYLog2;
int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
uvec4 value = imageLoad(imgIn, ivec2(coords));
imageStore(imgOut, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx, value);
}";
private readonly OpenGLRenderer _renderer;
private int[] _msToNonMSProgramHandles;
private int[] _nonMSToMSProgramHandles;
public TextureCopyMS(OpenGLRenderer renderer)
{
_renderer = renderer;
_msToNonMSProgramHandles = new int[5];
_nonMSToMSProgramHandles = new int[5];
}
public void CopyMSToNonMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
{
TextureCreateInfo srcInfo = src.Info;
TextureCreateInfo dstInfo = dst.Info;
int srcHandle = CreateViewIfNeeded(src);
int dstHandle = CreateViewIfNeeded(dst);
int dstWidth = dstInfo.Width;
int dstHeight = dstInfo.Height;
GL.UseProgram(GetMSToNonMSShader(srcInfo.BytesPerPixel));
for (int z = 0; z < depth; z++)
{
GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
GL.DispatchCompute((dstWidth + 31) / 32, (dstHeight + 31) / 32, 1);
}
Pipeline pipeline = (Pipeline)_renderer.Pipeline;
pipeline.RestoreProgram();
pipeline.RestoreImages1And2();
DestroyViewIfNeeded(src, srcHandle);
DestroyViewIfNeeded(dst, dstHandle);
}
public void CopyNonMSToMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
{
TextureCreateInfo srcInfo = src.Info;
TextureCreateInfo dstInfo = dst.Info;
int srcHandle = CreateViewIfNeeded(src);
int dstHandle = CreateViewIfNeeded(dst);
int srcWidth = srcInfo.Width;
int srcHeight = srcInfo.Height;
GL.UseProgram(GetNonMSToMSShader(srcInfo.BytesPerPixel));
for (int z = 0; z < depth; z++)
{
GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
GL.DispatchCompute((srcWidth + 31) / 32, (srcHeight + 31) / 32, 1);
}
Pipeline pipeline = (Pipeline)_renderer.Pipeline;
pipeline.RestoreProgram();
pipeline.RestoreImages1And2();
DestroyViewIfNeeded(src, srcHandle);
DestroyViewIfNeeded(dst, dstHandle);
}
private static SizedInternalFormat GetFormat(int bytesPerPixel)
{
return bytesPerPixel switch
{
1 => SizedInternalFormat.R8ui,
2 => SizedInternalFormat.R16ui,
4 => SizedInternalFormat.R32ui,
8 => SizedInternalFormat.Rg32ui,
16 => SizedInternalFormat.Rgba32ui,
_ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.")
};
}
private static int CreateViewIfNeeded(ITextureInfo texture)
{
// Binding sRGB textures as images doesn't work on NVIDIA,
// we need to create and bind a RGBA view for it to work.
if (texture.Info.Format == Format.R8G8B8A8Srgb)
{
int handle = GL.GenTexture();
GL.TextureView(
handle,
texture.Info.Target.Convert(),
texture.Storage.Handle,
PixelInternalFormat.Rgba8,
texture.FirstLevel,
1,
texture.FirstLayer,
texture.Info.GetLayers());
return handle;
}
return texture.Handle;
}
private static void DestroyViewIfNeeded(ITextureInfo info, int handle)
{
if (info.Handle != handle)
{
GL.DeleteTexture(handle);
}
}
private int GetMSToNonMSShader(int bytesPerPixel)
{
return GetShader(ComputeShaderMSToNonMS, _msToNonMSProgramHandles, bytesPerPixel);
}
private int GetNonMSToMSShader(int bytesPerPixel)
{
return GetShader(ComputeShaderNonMSToMS, _nonMSToMSProgramHandles, bytesPerPixel);
}
private int GetShader(string code, int[] programHandles, int bytesPerPixel)
{
int index = BitOperations.Log2((uint)bytesPerPixel);
if (programHandles[index] == 0)
{
int csHandle = GL.CreateShader(ShaderType.ComputeShader);
string format = new[] { "r8ui", "r16ui", "r32ui", "rg32ui", "rgba32ui" }[index];
GL.ShaderSource(csHandle, code.Replace("$FORMAT$", format));
GL.CompileShader(csHandle);
int programHandle = GL.CreateProgram();
GL.AttachShader(programHandle, csHandle);
GL.LinkProgram(programHandle);
GL.DetachShader(programHandle, csHandle);
GL.DeleteShader(csHandle);
GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status);
if (status == 0)
{
throw new Exception(GL.GetProgramInfoLog(programHandle));
}
programHandles[index] = programHandle;
}
return programHandles[index];
}
public void Dispose()
{
for (int i = 0; i < _msToNonMSProgramHandles.Length; i++)
{
if (_msToNonMSProgramHandles[i] != 0)
{
GL.DeleteProgram(_msToNonMSProgramHandles[i]);
_msToNonMSProgramHandles[i] = 0;
}
}
for (int i = 0; i < _nonMSToMSProgramHandles.Length; i++)
{
if (_nonMSToMSProgramHandles[i] != 0)
{
GL.DeleteProgram(_nonMSToMSProgramHandles[i]);
_nonMSToMSProgramHandles[i] = 0;
}
}
}
}
}

View File

@@ -116,28 +116,15 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
TextureView destinationView = (TextureView)destination; TextureView destinationView = (TextureView)destination;
if (destinationView.Target.IsMultisample() || Target.IsMultisample()) if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
{ {
Extents2D srcRegion = new Extents2D(0, 0, Width, Height); int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers);
}
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast( else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
GetIntermmediateTarget(Target), {
Info.BlockWidth, int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
Info.BlockHeight, _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers);
Info.BytesPerPixel,
Format,
Width,
Height,
Info.Depth,
Info.Levels);
GL.Disable(EnableCap.FramebufferSrgb);
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true);
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel);
GL.Enable(EnableCap.FramebufferSrgb);
} }
else else
{ {
@@ -149,28 +136,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
TextureView destinationView = (TextureView)destination; TextureView destinationView = (TextureView)destination;
if (destinationView.Target.IsMultisample() || Target.IsMultisample()) if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
{ {
Extents2D srcRegion = new Extents2D(0, 0, Width, Height); _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer,1);
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); }
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast( {
GetIntermmediateTarget(Target), _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1);
Info.BlockWidth,
Info.BlockHeight,
Info.BytesPerPixel,
Format,
Math.Max(1, Width >> srcLevel),
Math.Max(1, Height >> srcLevel),
1,
1);
GL.Disable(EnableCap.FramebufferSrgb);
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1);
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1);
GL.Enable(EnableCap.FramebufferSrgb);
} }
else else
{ {
@@ -178,17 +150,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
} }
} }
private static Target GetIntermmediateTarget(Target srcTarget)
{
return srcTarget switch
{
Target.Texture2D => Target.Texture2DMultisample,
Target.Texture2DArray => Target.Texture2DMultisampleArray,
Target.Texture2DMultisampleArray => Target.Texture2DArray,
_ => Target.Texture2D
};
}
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
{ {
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter); _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);

View File

@@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.OpenGL
private TextureCopy _textureCopy; private TextureCopy _textureCopy;
private TextureCopy _backgroundTextureCopy; private TextureCopy _backgroundTextureCopy;
internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy; internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy;
internal TextureCopyMS TextureCopyMS { get; }
private Sync _sync; private Sync _sync;
@@ -48,6 +49,7 @@ namespace Ryujinx.Graphics.OpenGL
_window = new Window(this); _window = new Window(this);
_textureCopy = new TextureCopy(this); _textureCopy = new TextureCopy(this);
_backgroundTextureCopy = new TextureCopy(this); _backgroundTextureCopy = new TextureCopy(this);
TextureCopyMS = new TextureCopyMS(this);
_sync = new Sync(); _sync = new Sync();
PersistentBuffers = new PersistentBuffers(); PersistentBuffers = new PersistentBuffers();
ResourcePool = new ResourcePool(); ResourcePool = new ResourcePool();
@@ -211,6 +213,7 @@ namespace Ryujinx.Graphics.OpenGL
{ {
_textureCopy.Dispose(); _textureCopy.Dispose();
_backgroundTextureCopy.Dispose(); _backgroundTextureCopy.Dispose();
TextureCopyMS.Dispose();
PersistentBuffers.Dispose(); PersistentBuffers.Dispose();
ResourcePool.Dispose(); ResourcePool.Dispose();
_pipeline.Dispose(); _pipeline.Dispose();

View File

@@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.OpenGL
{ {
class Pipeline : IPipeline, IDisposable class Pipeline : IPipeline, IDisposable
{ {
private const int SavedImages = 2;
private readonly DrawTextureEmulation _drawTexture; private readonly DrawTextureEmulation _drawTexture;
internal ulong DrawCount { get; private set; } internal ulong DrawCount { get; private set; }
@@ -46,6 +48,7 @@ namespace Ryujinx.Graphics.OpenGL
private Vector4<float>[] _renderScale = new Vector4<float>[73]; private Vector4<float>[] _renderScale = new Vector4<float>[73];
private int _fragmentScaleCount; private int _fragmentScaleCount;
private (TextureBase, Format)[] _images;
private TextureBase _unit0Texture; private TextureBase _unit0Texture;
private Sampler _unit0Sampler; private Sampler _unit0Sampler;
@@ -78,6 +81,8 @@ namespace Ryujinx.Graphics.OpenGL
_fragmentOutputMap = uint.MaxValue; _fragmentOutputMap = uint.MaxValue;
_componentMasks = uint.MaxValue; _componentMasks = uint.MaxValue;
_images = new (TextureBase, Format)[SavedImages];
var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f }; var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
new Span<Vector4<float>>(_renderScale).Fill(defaultScale); new Span<Vector4<float>>(_renderScale).Fill(defaultScale);
@@ -907,6 +912,11 @@ namespace Ryujinx.Graphics.OpenGL
public void SetImage(int binding, ITexture texture, Format imageFormat) public void SetImage(int binding, ITexture texture, Format imageFormat)
{ {
if ((uint)binding < SavedImages)
{
_images[binding] = (texture as TextureBase, imageFormat);
}
if (texture == null) if (texture == null)
{ {
return; return;
@@ -1608,6 +1618,32 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
public void RestoreProgram()
{
_program?.Bind();
}
public void RestoreImages1And2()
{
for (int i = 0; i < SavedImages; i++)
{
(TextureBase texBase, Format imageFormat) = _images[i];
if (texBase != null)
{
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
if (format != 0)
{
GL.BindImageTexture(i, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format);
continue;
}
}
GL.BindImageTexture(i, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
}
}
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
{ {
if (value is CounterQueueEvent) if (value is CounterQueueEvent)

View File

@@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader
{ {
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = UnpackOffsets(wordOffset); (int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = UnpackOffsets(wordOffset);
int handle = cachedTextureBuffer[textureWordOffset]; int handle = cachedTextureBuffer.Length != 0 ? cachedTextureBuffer[textureWordOffset] : 0;
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader) // The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses // is a 13-bit value. However, in order to also support separate samplers and textures (which uses
@@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.Shader
if (handleType != TextureHandleType.SeparateConstantSamplerHandle) if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
{ {
samplerHandle = cachedSamplerBuffer[samplerWordOffset]; samplerHandle = cachedSamplerBuffer.Length != 0 ? cachedSamplerBuffer[samplerWordOffset] : 0;
} }
else else
{ {

View File

@@ -370,6 +370,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3); holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3);
_gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size); _gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
_cachedConvertedBuffers.Add(offset, size, key, holder); _cachedConvertedBuffers.Add(offset, size, key, holder);
@@ -388,6 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride); holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride);
_gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride); _gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride);
key.SetBuffer(holder.GetBuffer()); key.SetBuffer(holder.GetBuffer());
@@ -398,6 +400,29 @@ namespace Ryujinx.Graphics.Vulkan
return holder.GetBuffer(); return holder.GetBuffer();
} }
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
{
// The destination index size is always I32.
int indexCount = size / indexSize;
int convertedCount = pattern.GetConvertedCount(indexCount);
holder = _gd.BufferManager.Create(_gd, convertedCount * 4);
_gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ConvertIndexBuffer(_gd, cbs, this, holder, pattern, indexSize, offset, indexCount);
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
}
public void Dispose() public void Dispose()
{ {
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);

View File

@@ -140,6 +140,16 @@ namespace Ryujinx.Graphics.Vulkan
return null; return null;
} }
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
}
return null;
}
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size) public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
{ {
if (TryGetBuffer(handle, out var holder)) if (TryGetBuffer(handle, out var holder))

View File

@@ -1,5 +1,4 @@
using Silk.NET.Vulkan; using System;
using System;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -9,38 +8,17 @@ namespace Ryujinx.Graphics.Vulkan
private readonly int _offset; private readonly int _offset;
private readonly int _size; private readonly int _size;
private readonly IndexType _type;
private readonly Auto<DisposableBuffer> _buffer; private readonly Auto<DisposableBuffer> _buffer;
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, IndexType type)
{
_buffer = buffer;
_offset = offset;
_size = size;
_type = type;
buffer?.IncrementReferenceCount();
}
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size) public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
{ {
_buffer = buffer; _buffer = buffer;
_offset = offset; _offset = offset;
_size = size; _size = size;
_type = IndexType.Uint16;
buffer?.IncrementReferenceCount(); buffer?.IncrementReferenceCount();
} }
public void BindIndexBuffer(Vk api, CommandBufferScoped cbs)
{
if (_buffer != null)
{
api.CmdBindIndexBuffer(cbs.CommandBuffer, _buffer.Get(cbs, _offset, _size).Value, (ulong)_offset, _type);
}
}
public void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding) public void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding)
{ {
if (_buffer != null) if (_buffer != null)

View File

@@ -10,14 +10,25 @@ namespace Ryujinx.Graphics.Vulkan
struct I8ToI16CacheKey : ICacheKey struct I8ToI16CacheKey : ICacheKey
{ {
public I8ToI16CacheKey() { } // Used to notify the pipeline that bindings have invalidated on dispose.
private readonly VulkanRenderer _gd;
private Auto<DisposableBuffer> _buffer;
public I8ToI16CacheKey(VulkanRenderer gd)
{
_gd = gd;
_buffer = null;
}
public bool KeyEqual(ICacheKey other) public bool KeyEqual(ICacheKey other)
{ {
return other is I8ToI16CacheKey; return other is I8ToI16CacheKey;
} }
public void Dispose() { } public void Dispose()
{
_gd.PipelineInternal.DirtyIndexBuffer(_buffer);
}
} }
struct AlignedVertexBufferCacheKey : ICacheKey struct AlignedVertexBufferCacheKey : ICacheKey
@@ -55,6 +66,41 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
struct TopologyConversionCacheKey : ICacheKey
{
private IndexBufferPattern _pattern;
private int _indexSize;
// Used to notify the pipeline that bindings have invalidated on dispose.
private readonly VulkanRenderer _gd;
private Auto<DisposableBuffer> _buffer;
public TopologyConversionCacheKey(VulkanRenderer gd, IndexBufferPattern pattern, int indexSize)
{
_gd = gd;
_pattern = pattern;
_indexSize = indexSize;
_buffer = null;
}
public bool KeyEqual(ICacheKey other)
{
return other is TopologyConversionCacheKey entry &&
entry._pattern == _pattern &&
entry._indexSize == _indexSize;
}
public void SetBuffer(Auto<DisposableBuffer> buffer)
{
_buffer = buffer;
}
public void Dispose()
{
_gd.PipelineInternal.DirtyIndexBuffer(_buffer);
}
}
struct CacheByRange<T> where T : IDisposable struct CacheByRange<T> where T : IDisposable
{ {
private struct Entry private struct Entry

View File

@@ -2,6 +2,7 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -179,8 +180,8 @@ namespace Ryujinx.Graphics.Vulkan
GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency, GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency,
GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency, GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency,
GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList, GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList,
GAL.PrimitiveTopology.Quads => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan, // Emulated with triangle fans GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."),
GAL.PrimitiveTopology.QuadStrip => Silk.NET.Vulkan.PrimitiveTopology.TriangleStrip, // Emulated with triangle strips GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."),
_ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList) _ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList)
}; };
} }

View File

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using Ryujinx.Graphics.Vulkan.Shaders; using Ryujinx.Graphics.Vulkan.Shaders;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic;
using VkFormat = Silk.NET.Vulkan.Format; using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
@@ -399,6 +400,86 @@ namespace Ryujinx.Graphics.Vulkan
newSize); newSize);
} }
public unsafe void ConvertIndexBuffer(VulkanRenderer gd,
CommandBufferScoped cbs,
BufferHolder src,
BufferHolder dst,
IndexBufferPattern pattern,
int indexSize,
int srcOffset,
int indexCount)
{
int convertedCount = pattern.GetConvertedCount(indexCount);
int outputIndexSize = 4;
// TODO: Do this with a compute shader?
var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
var bufferCopy = new List<BufferCopy>();
int outputOffset = 0;
// Try to merge copies of adjacent indices to reduce copy count.
int sequenceStart = 0;
int sequenceLength = 0;
foreach (var index in pattern.GetIndexMapping(indexCount))
{
if (sequenceLength > 0)
{
if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize)
{
sequenceLength++;
continue;
}
// Commit the copy so far.
bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
outputOffset += outputIndexSize * sequenceLength;
}
sequenceStart = index;
sequenceLength = 1;
}
if (sequenceLength > 0)
{
// Commit final pending copy.
bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
}
var bufferCopyArray = bufferCopy.ToArray();
BufferHolder.InsertBufferBarrier(
gd,
cbs.CommandBuffer,
dstBuffer,
BufferHolder.DefaultAccessFlags,
AccessFlags.AccessTransferWriteBit,
PipelineStageFlags.PipelineStageAllCommandsBit,
PipelineStageFlags.PipelineStageTransferBit,
0,
convertedCount * outputIndexSize);
fixed (BufferCopy* pBufferCopy = bufferCopyArray)
{
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy);
}
BufferHolder.InsertBufferBarrier(
gd,
cbs.CommandBuffer,
dstBuffer,
AccessFlags.AccessTransferWriteBit,
BufferHolder.DefaultAccessFlags,
PipelineStageFlags.PipelineStageTransferBit,
PipelineStageFlags.PipelineStageAllCommandsBit,
0,
convertedCount * outputIndexSize);
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)

View File

@@ -0,0 +1,139 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
internal class IndexBufferPattern : IDisposable
{
public int PrimitiveVertices { get; }
public int PrimitiveVerticesOut { get; }
public int BaseIndex { get; }
public int[] OffsetIndex { get; }
public int IndexStride { get; }
public bool RepeatStart { get; }
private VulkanRenderer _gd;
private int _currentSize;
private BufferHandle _repeatingBuffer;
public IndexBufferPattern(VulkanRenderer gd,
int primitiveVertices,
int primitiveVerticesOut,
int baseIndex,
int[] offsetIndex,
int indexStride,
bool repeatStart)
{
PrimitiveVertices = primitiveVertices;
PrimitiveVerticesOut = primitiveVerticesOut;
BaseIndex = baseIndex;
OffsetIndex = offsetIndex;
IndexStride = indexStride;
RepeatStart = repeatStart;
_gd = gd;
}
public int GetPrimitiveCount(int vertexCount)
{
return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride);
}
public int GetConvertedCount(int indexCount)
{
int primitiveCount = GetPrimitiveCount(indexCount);
return primitiveCount * OffsetIndex.Length;
}
public IEnumerable<int> GetIndexMapping(int indexCount)
{
int primitiveCount = GetPrimitiveCount(indexCount);
int index = BaseIndex;
for (int i = 0; i < primitiveCount; i++)
{
if (RepeatStart)
{
// Used for triangle fan
yield return 0;
}
for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
{
yield return index + OffsetIndex[j];
}
index += IndexStride;
}
}
public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount)
{
int primitiveCount = GetPrimitiveCount(vertexCount);
indexCount = primitiveCount * PrimitiveVerticesOut;
int expectedSize = primitiveCount * OffsetIndex.Length;
if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null)
{
return _repeatingBuffer;
}
// Expand the repeating pattern to the number of requested primitives.
BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));
// Copy the old data to the new one.
if (_repeatingBuffer != BufferHandle.Null)
{
_gd.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int));
_gd.DeleteBuffer(_repeatingBuffer);
}
_repeatingBuffer = newBuffer;
// Add the additional repeats on top.
int newPrimitives = primitiveCount;
int oldPrimitives = (_currentSize) / OffsetIndex.Length;
int[] newData;
newPrimitives -= oldPrimitives;
newData = new int[expectedSize - _currentSize];
int outOffset = 0;
int index = oldPrimitives * IndexStride + BaseIndex;
for (int i = 0; i < newPrimitives; i++)
{
if (RepeatStart)
{
// Used for triangle fan
newData[outOffset++] = 0;
}
for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
{
newData[outOffset++] = index + OffsetIndex[j];
}
index += IndexStride;
}
_gd.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast<int, byte>(newData));
_currentSize = expectedSize;
return newBuffer;
}
public void Dispose()
{
if (_repeatingBuffer != BufferHandle.Null)
{
_gd.DeleteBuffer(_repeatingBuffer);
_repeatingBuffer = BufferHandle.Null;
}
}
}
}

View File

@@ -0,0 +1,97 @@
using Silk.NET.Vulkan;
using System;
namespace Ryujinx.Graphics.Vulkan
{
internal struct IndexBufferState
{
public static IndexBufferState Null => new IndexBufferState(GAL.BufferHandle.Null, 0, 0);
private readonly int _offset;
private readonly int _size;
private readonly IndexType _type;
private readonly GAL.BufferHandle _handle;
private Auto<DisposableBuffer> _buffer;
public IndexBufferState(GAL.BufferHandle handle, int offset, int size, IndexType type)
{
_handle = handle;
_offset = offset;
_size = size;
_type = type;
_buffer = null;
}
public IndexBufferState(GAL.BufferHandle handle, int offset, int size)
{
_handle = handle;
_offset = offset;
_size = size;
_type = IndexType.Uint16;
_buffer = null;
}
public void BindIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs)
{
Auto<DisposableBuffer> autoBuffer;
int offset, size;
IndexType type = _type;
if (_type == IndexType.Uint8Ext && !gd.Capabilities.SupportsIndexTypeUint8)
{
// Index type is not supported. Convert to I16.
autoBuffer = gd.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size);
type = IndexType.Uint16;
offset = 0;
size = _size * 2;
}
else
{
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
offset = _offset;
size = _size;
}
_buffer = autoBuffer;
if (autoBuffer != null)
{
gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, offset, size).Value, (ulong)offset, type);
}
}
public void BindConvertedIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, int firstIndex, int indexCount, int convertedCount, IndexBufferPattern pattern)
{
Auto<DisposableBuffer> autoBuffer;
// Convert the index buffer using the given pattern.
int indexSize = _type switch
{
IndexType.Uint32 => 4,
IndexType.Uint16 => 2,
_ => 1,
};
int firstIndexOffset = firstIndex * indexSize;
autoBuffer = gd.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize);
int size = convertedCount * 4;
_buffer = autoBuffer;
if (autoBuffer != null)
{
gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, 0, size).Value, 0, IndexType.Uint32);
}
}
public bool BoundEquals(Auto<DisposableBuffer> buffer)
{
return _buffer == buffer;
}
}
}

View File

@@ -51,13 +51,16 @@ namespace Ryujinx.Graphics.Vulkan
private readonly DescriptorSetUpdater _descriptorSetUpdater; private readonly DescriptorSetUpdater _descriptorSetUpdater;
private BufferState _indexBuffer; private IndexBufferState _indexBuffer;
private IndexBufferPattern _indexBufferPattern;
private readonly BufferState[] _transformFeedbackBuffers; private readonly BufferState[] _transformFeedbackBuffers;
private readonly VertexBufferState[] _vertexBuffers; private readonly VertexBufferState[] _vertexBuffers;
private ulong _vertexBuffersDirty; private ulong _vertexBuffersDirty;
protected Rectangle<int> ClearScissor; protected Rectangle<int> ClearScissor;
public SupportBufferUpdater SupportBufferUpdater; public SupportBufferUpdater SupportBufferUpdater;
public IndexBufferPattern QuadsToTrisPattern;
public IndexBufferPattern TriFanToTrisPattern;
private bool _needsIndexBufferRebind; private bool _needsIndexBufferRebind;
private bool _needsTransformFeedbackBuffersRebind; private bool _needsTransformFeedbackBuffersRebind;
@@ -107,6 +110,9 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SupportBufferUpdater = new SupportBufferUpdater(Gd); SupportBufferUpdater = new SupportBufferUpdater(Gd);
SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount); SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
} }
public unsafe void Barrier() public unsafe void Barrier()
@@ -245,6 +251,14 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
public void DirtyIndexBuffer(Auto<DisposableBuffer> buffer)
{
if (_indexBuffer.BoundEquals(buffer))
{
_needsIndexBufferRebind = true;
}
}
public void DispatchCompute(int groupsX, int groupsY, int groupsZ) public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
{ {
if (!_program.IsLinked) if (!_program.IsLinked)
@@ -267,24 +281,59 @@ namespace Ryujinx.Graphics.Vulkan
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass(); BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++; DrawCount++;
if (_topology == GAL.PrimitiveTopology.Quads) if (Gd.TopologyUnsupported(_topology))
{ {
int quadsCount = vertexCount / 4; // Temporarily bind a conversion pattern as an index buffer.
_needsIndexBufferRebind = true;
for (int i = 0; i < quadsCount; i++) IndexBufferPattern pattern = _topology switch
{ {
Gd.Api.CmdDraw(CommandBuffer, 4, (uint)instanceCount, (uint)(firstVertex + i * 4), (uint)firstInstance); GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
} GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
};
BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount);
var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, handle, false);
Gd.Api.CmdBindIndexBuffer(CommandBuffer, buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value, 0, Silk.NET.Vulkan.IndexType.Uint32);
BeginRenderPass(); // May have been interrupted to set buffer data.
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
} }
else else
{ {
ResumeTransformFeedbackInternal();
Gd.Api.CmdDraw(CommandBuffer, (uint)vertexCount, (uint)instanceCount, (uint)firstVertex, (uint)firstInstance); Gd.Api.CmdDraw(CommandBuffer, (uint)vertexCount, (uint)instanceCount, (uint)firstVertex, (uint)firstInstance);
} }
} }
private void UpdateIndexBufferPattern()
{
IndexBufferPattern pattern = null;
if (Gd.TopologyUnsupported(_topology))
{
pattern = _topology switch
{
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
};
}
if (_indexBufferPattern != pattern)
{
_indexBufferPattern = pattern;
_needsIndexBufferRebind = true;
}
}
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
{ {
if (!_program.IsLinked) if (!_program.IsLinked)
@@ -292,22 +341,34 @@ namespace Ryujinx.Graphics.Vulkan
return; return;
} }
UpdateIndexBufferPattern();
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass(); BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++; DrawCount++;
if (_topology == GAL.PrimitiveTopology.Quads) if (_indexBufferPattern != null)
{ {
int quadsCount = indexCount / 4; // Convert the index buffer into a supported topology.
IndexBufferPattern pattern = _indexBufferPattern;
for (int i = 0; i < quadsCount; i++) int convertedCount = pattern.GetConvertedCount(indexCount);
if (_needsIndexBufferRebind)
{ {
Gd.Api.CmdDrawIndexed(CommandBuffer, 4, (uint)instanceCount, (uint)(firstIndex + i * 4), firstVertex, (uint)firstInstance); _indexBuffer.BindConvertedIndexBuffer(Gd, Cbs, firstIndex, indexCount, convertedCount, pattern);
_needsIndexBufferRebind = false;
} }
BeginRenderPass(); // May have been interrupted to set buffer data.
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)convertedCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
} }
else else
{ {
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, (uint)firstIndex, firstVertex, (uint)firstInstance); Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, (uint)firstIndex, firstVertex, (uint)firstInstance);
} }
} }
@@ -500,34 +561,16 @@ namespace Ryujinx.Graphics.Vulkan
public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type) public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type)
{ {
_indexBuffer.Dispose();
if (buffer.Handle != BufferHandle.Null) if (buffer.Handle != BufferHandle.Null)
{ {
Auto<DisposableBuffer> ib = null; _indexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type.Convert());
int offset = buffer.Offset;
int size = buffer.Size;
if (type == GAL.IndexType.UByte && !Gd.Capabilities.SupportsIndexTypeUint8)
{
ib = Gd.BufferManager.GetBufferI8ToI16(Cbs, buffer.Handle, offset, size);
offset = 0;
size *= 2;
type = GAL.IndexType.UShort;
}
else
{
ib = Gd.BufferManager.GetBuffer(CommandBuffer, buffer.Handle, false);
}
_indexBuffer = new BufferState(ib, offset, size, type.Convert());
} }
else else
{ {
_indexBuffer = BufferState.Null; _indexBuffer = IndexBufferState.Null;
} }
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs); _needsIndexBufferRebind = true;
} }
public void SetLineParameters(float width, bool smooth) public void SetLineParameters(float width, bool smooth)
@@ -584,7 +627,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_topology = topology; _topology = topology;
var vkTopology = topology.Convert(); var vkTopology = Gd.TopologyRemap(topology).Convert();
_newState.Topology = vkTopology; _newState.Topology = vkTopology;
@@ -1127,9 +1170,9 @@ namespace Ryujinx.Graphics.Vulkan
// Commit changes to the support buffer before drawing. // Commit changes to the support buffer before drawing.
SupportBufferUpdater.Commit(); SupportBufferUpdater.Commit();
if (_needsIndexBufferRebind) if (_needsIndexBufferRebind && _indexBufferPattern == null)
{ {
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs); _indexBuffer.BindIndexBuffer(Gd, Cbs);
_needsIndexBufferRebind = false; _needsIndexBufferRebind = false;
} }
@@ -1265,7 +1308,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_renderPass?.Dispose(); _renderPass?.Dispose();
_framebuffer?.Dispose(); _framebuffer?.Dispose();
_indexBuffer.Dispose();
_newState.Dispose(); _newState.Dispose();
_descriptorSetUpdater.Dispose(); _descriptorSetUpdater.Dispose();

View File

@@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.StencilTestEnable = state.StencilTest.TestEnable; pipeline.StencilTestEnable = state.StencilTest.TestEnable;
pipeline.Topology = state.Topology.Convert(); pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount); int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);

View File

@@ -471,6 +471,25 @@ 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)
{
return topology switch
{
GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles,
GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip,
_ => topology
};
}
public bool TopologyUnsupported(GAL.PrimitiveTopology topology)
{
return topology switch
{
GAL.PrimitiveTopology.Quads => true,
_ => false
};
}
public void Initialize(GraphicsDebugLevel logLevel) public void Initialize(GraphicsDebugLevel logLevel)
{ {
SetupContext(logLevel); SetupContext(logLevel);