mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2025-12-12 23:22:05 +01:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cfec5de4b | ||
|
|
37b6e081da | ||
|
|
3c3bcd82fe |
@@ -577,5 +577,7 @@
|
|||||||
"UserProfileNoImageError": "Profile image must be set",
|
"UserProfileNoImageError": "Profile image must be set",
|
||||||
"GameUpdateWindowHeading": "Updates Available for {0} [{1}]",
|
"GameUpdateWindowHeading": "Updates Available for {0} [{1}]",
|
||||||
"SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:",
|
"SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:",
|
||||||
"SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:"
|
"SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:",
|
||||||
|
"UserProfilesName": "Name:",
|
||||||
|
"UserProfilesUserId" : "User Id:"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl
|
||||||
|
x:Class="Ryujinx.Ava.Ui.Controls.UserEditor"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
||||||
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"
|
||||||
mc:Ignorable="d"
|
|
||||||
Padding="0"
|
|
||||||
Margin="0"
|
|
||||||
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
|
||||||
xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models"
|
xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models"
|
||||||
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
x:Class="Ryujinx.Ava.Ui.Controls.UserEditor">
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||||
|
Margin="0"
|
||||||
|
Padding="0"
|
||||||
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
@@ -23,33 +24,64 @@
|
|||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Left">
|
<StackPanel
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Orientation="Vertical">
|
||||||
<Image
|
<Image
|
||||||
|
Name="ProfileImage"
|
||||||
|
Width="96"
|
||||||
|
Height="96"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
Height="96" Width="96"
|
|
||||||
Name="ProfileImage"
|
|
||||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||||
<Button Margin="5" Content="{Locale:Locale UserProfilesChangeProfileImage}"
|
<Button
|
||||||
Name="ChangePictureButton"
|
Name="ChangePictureButton"
|
||||||
|
Margin="5"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
Click="ChangePictureButton_Click"
|
Click="ChangePictureButton_Click"
|
||||||
HorizontalAlignment="Stretch"/>
|
Content="{Locale:Locale UserProfilesChangeProfileImage}" />
|
||||||
<Button Margin="5" Content="{Locale:Locale UserProfilesSetProfileImage}"
|
<Button
|
||||||
Name="AddPictureButton"
|
Name="AddPictureButton"
|
||||||
|
Margin="5"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
Click="ChangePictureButton_Click"
|
Click="ChangePictureButton_Click"
|
||||||
HorizontalAlignment="Stretch"/>
|
Content="{Locale:Locale UserProfilesSetProfileImage}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Orientation="Vertical" HorizontalAlignment="Stretch" Grid.Column="1" Spacing="10"
|
<StackPanel
|
||||||
Margin="5, 10">
|
Grid.Row="0"
|
||||||
<TextBox Name="NameBox" Width="300" Text="{Binding Name}" MaxLength="{Binding MaxProfileNameLength}"
|
Grid.Column="1"
|
||||||
HorizontalAlignment="Stretch" />
|
Margin="5,10"
|
||||||
<TextBlock Text="{Binding UserId}" Name="IdLabel" />
|
HorizontalAlignment="Stretch"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="10">
|
||||||
|
<TextBlock Text="{Locale:Locale UserProfilesName}" />
|
||||||
|
<TextBox
|
||||||
|
Name="NameBox"
|
||||||
|
Width="300"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
MaxLength="{Binding MaxProfileNameLength}"
|
||||||
|
Text="{Binding Name}" />
|
||||||
|
<TextBlock Text="{Locale:Locale UserProfilesUserId}" />
|
||||||
|
<TextBlock Name="IdLabel" Text="{Binding UserId}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
|
<StackPanel
|
||||||
<Button Content="{Locale:Locale Save}" Name="SaveButton" Click="SaveButton_Click"/>
|
Grid.Row="1"
|
||||||
<Button HorizontalAlignment="Right" Content="{Locale:Locale Discard}"
|
Grid.Column="0"
|
||||||
Name="CloseButton" Click="CloseButton_Click"/>
|
Grid.ColumnSpan="2"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="10">
|
||||||
|
<Button
|
||||||
|
Name="SaveButton"
|
||||||
|
Click="SaveButton_Click"
|
||||||
|
Content="{Locale:Locale Save}" />
|
||||||
|
<Button
|
||||||
|
Name="CloseButton"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Click="CloseButton_Click"
|
||||||
|
Content="{Locale:Locale Discard}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
_parent?.GoBack();
|
_parent?.GoBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
private async void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
DataValidationErrors.ClearErrors(NameBox);
|
DataValidationErrors.ClearErrors(NameBox);
|
||||||
bool isInvalid = false;
|
bool isInvalid = false;
|
||||||
@@ -77,7 +77,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
|
|
||||||
if (TempProfile.Image == null)
|
if (TempProfile.Image == null)
|
||||||
{
|
{
|
||||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UserProfileNoImageError"], "");
|
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UserProfileNoImageError"], "");
|
||||||
|
|
||||||
isInvalid = true;
|
isInvalid = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,34 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl
|
||||||
|
x:Class="Ryujinx.Ava.Ui.Controls.UserSelector"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
|
||||||
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
|
||||||
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
||||||
|
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"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||||
x:Class="Ryujinx.Ava.Ui.Controls.UserSelector">
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Design.DataContext>
|
<Design.DataContext>
|
||||||
<viewModels:UserProfileViewModel />
|
<viewModels:UserProfileViewModel />
|
||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
<Grid HorizontalAlignment="Stretch"
|
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
VerticalAlignment="Stretch">
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5" Items="{Binding Profiles}"
|
<ListBox
|
||||||
|
Margin="5"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Center"
|
||||||
DoubleTapped="ProfilesList_DoubleTapped"
|
DoubleTapped="ProfilesList_DoubleTapped"
|
||||||
|
Items="{Binding Profiles}"
|
||||||
SelectionChanged="SelectingItemsControl_SelectionChanged">
|
SelectionChanged="SelectingItemsControl_SelectionChanged">
|
||||||
<ListBox.ItemsPanel>
|
<ListBox.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
@@ -49,10 +55,11 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Image
|
<Image
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
Width="96"
|
||||||
|
Height="96"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
Height="96" Width="96"
|
|
||||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
@@ -68,23 +75,34 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
<Border HorizontalAlignment="Left" VerticalAlignment="Top"
|
<Border
|
||||||
IsVisible="{Binding IsOpened}"
|
|
||||||
Background="LimeGreen"
|
|
||||||
Width="10"
|
Width="10"
|
||||||
Height="10"
|
Height="10"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
CornerRadius="5" />
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Background="LimeGreen"
|
||||||
|
CornerRadius="5"
|
||||||
|
IsVisible="{Binding IsOpened}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10,0" Spacing="10" HorizontalAlignment="Center">
|
<StackPanel
|
||||||
<Button Content="{Locale:Locale UserProfilesAddNewProfile}" Command="{Binding AddUser}" />
|
Grid.Row="1"
|
||||||
<Button IsEnabled="{Binding IsSelectedProfiledEditable}"
|
Margin="10,0"
|
||||||
Content="{Locale:Locale UserProfilesEditProfile}" Command="{Binding EditUser}" />
|
HorizontalAlignment="Center"
|
||||||
<Button IsEnabled="{Binding IsSelectedProfileDeletable}"
|
Orientation="Horizontal"
|
||||||
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}" Command="{Binding DeleteUser}" />
|
Spacing="10">
|
||||||
|
<Button Command="{Binding AddUser}" Content="{Locale:Locale UserProfilesAddNewProfile}" />
|
||||||
|
<Button
|
||||||
|
Command="{Binding EditUser}"
|
||||||
|
Content="{Locale:Locale UserProfilesEditProfile}"
|
||||||
|
IsEnabled="{Binding IsSelectedProfiledEditable}" />
|
||||||
|
<Button
|
||||||
|
Command="{Binding DeleteUser}"
|
||||||
|
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}"
|
||||||
|
IsEnabled="{Binding IsSelectedProfileDeletable}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -21,7 +21,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
||||||
{
|
{
|
||||||
NavigatedTo(e);
|
NavigatedTo(e);
|
||||||
}, Avalonia.Interactivity.RoutingStrategies.Direct);
|
}, RoutingStrategies.Direct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,12 +29,10 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
{
|
{
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
switch (arg.NavigationMode)
|
if (arg.NavigationMode == NavigationMode.New)
|
||||||
{
|
{
|
||||||
case NavigationMode.New:
|
|
||||||
_parent = (NavigationDialogHost)arg.Parameter;
|
_parent = (NavigationDialogHost)arg.Parameter;
|
||||||
ViewModel = _parent.ViewModel;
|
ViewModel = _parent.ViewModel;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataContext = ViewModel;
|
DataContext = ViewModel;
|
||||||
|
|||||||
@@ -43,11 +43,9 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsHighlightedProfileEditable =>
|
public bool IsHighlightedProfileEditable => _highlightedProfile != null;
|
||||||
_highlightedProfile != null;
|
|
||||||
|
|
||||||
public bool IsHighlightedProfileDeletable =>
|
public bool IsHighlightedProfileDeletable => _highlightedProfile != null && _highlightedProfile.UserId != AccountManager.DefaultUserId;
|
||||||
_highlightedProfile != null && _highlightedProfile.UserId != AccountManager.DefaultUserId;
|
|
||||||
|
|
||||||
public UserProfile HighlightedProfile
|
public UserProfile HighlightedProfile
|
||||||
{
|
{
|
||||||
@@ -62,16 +60,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose() { }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadProfiles()
|
public void LoadProfiles()
|
||||||
{
|
{
|
||||||
Profiles.Clear();
|
Profiles.Clear();
|
||||||
|
|
||||||
var profiles = _owner.AccountManager.GetAllUsers()
|
var profiles = _owner.AccountManager.GetAllUsers().OrderByDescending(x => x.AccountState == AccountState.Open);
|
||||||
.OrderByDescending(x => x.AccountState == AccountState.Open);
|
|
||||||
|
|
||||||
foreach (var profile in profiles)
|
foreach (var profile in profiles)
|
||||||
{
|
{
|
||||||
@@ -94,6 +89,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
public void AddUser()
|
public void AddUser()
|
||||||
{
|
{
|
||||||
UserProfile userProfile = null;
|
UserProfile userProfile = null;
|
||||||
|
|
||||||
_owner.Navigate(typeof(UserEditor), (this._owner, userProfile, true));
|
_owner.Navigate(typeof(UserEditor), (this._owner, userProfile, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -216,13 +216,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
{
|
{
|
||||||
var target = memoryManager.Physical.TextureCache.FindTexture(
|
var target = memoryManager.Physical.TextureCache.FindTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
dst,
|
|
||||||
dstGpuVa,
|
dstGpuVa,
|
||||||
dstBpp,
|
dstBpp,
|
||||||
dstStride,
|
dstStride,
|
||||||
|
dst.Height,
|
||||||
xCount,
|
xCount,
|
||||||
yCount,
|
yCount,
|
||||||
dstLinear);
|
dstLinear,
|
||||||
|
dst.MemoryLayout);
|
||||||
|
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,9 +59,24 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
{
|
{
|
||||||
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
||||||
oldMemoryManager.Physical.DecrementReferenceCount();
|
oldMemoryManager.Physical.DecrementReferenceCount();
|
||||||
|
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
|
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
|
||||||
|
memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
|
||||||
|
|
||||||
|
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
||||||
|
TextureManager.ReloadPools();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Memory mappings change event handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Memory manager where the mappings changed</param>
|
||||||
|
/// <param name="e">Information about the region that is being changed</param>
|
||||||
|
private void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
|
||||||
|
{
|
||||||
|
TextureManager.ReloadPools();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
129
Ryujinx.Graphics.Gpu/Image/PoolCache.cs
Normal file
129
Ryujinx.Graphics.Gpu/Image/PoolCache.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Resource pool interface.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Resource pool type</typeparam>
|
||||||
|
interface IPool<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start address of the pool in memory.
|
||||||
|
/// </summary>
|
||||||
|
ulong Address { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Linked list node used on the texture pool cache.
|
||||||
|
/// </summary>
|
||||||
|
LinkedListNode<T> CacheNode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timestamp set on the last use of the pool by the cache.
|
||||||
|
/// </summary>
|
||||||
|
ulong CacheTimestamp { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pool cache.
|
||||||
|
/// This can keep multiple pools, and return the current one as needed.
|
||||||
|
/// </summary>
|
||||||
|
abstract class PoolCache<T> : IDisposable where T : IPool<T>, IDisposable
|
||||||
|
{
|
||||||
|
private const int MaxCapacity = 2;
|
||||||
|
private const ulong MinDeltaForRemoval = 20000;
|
||||||
|
|
||||||
|
private readonly GpuContext _context;
|
||||||
|
private readonly LinkedList<T> _pools;
|
||||||
|
private ulong _currentTimestamp;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a new instance of the pool.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
|
public PoolCache(GpuContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_pools = new LinkedList<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increments the internal timestamp of the cache that is used to decide when old resources will be deleted.
|
||||||
|
/// </summary>
|
||||||
|
public void Tick()
|
||||||
|
{
|
||||||
|
_currentTimestamp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a cache texture pool, or creates a new one if not found.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel">GPU channel that the texture pool cache belongs to</param>
|
||||||
|
/// <param name="address">Start address of the texture pool</param>
|
||||||
|
/// <param name="maximumId">Maximum ID of the texture pool</param>
|
||||||
|
/// <returns>The found or newly created texture pool</returns>
|
||||||
|
public T FindOrCreate(GpuChannel channel, ulong address, int maximumId)
|
||||||
|
{
|
||||||
|
// Remove old entries from the cache, if possible.
|
||||||
|
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
|
||||||
|
{
|
||||||
|
T oldestPool = _pools.First.Value;
|
||||||
|
|
||||||
|
_pools.RemoveFirst();
|
||||||
|
oldestPool.Dispose();
|
||||||
|
oldestPool.CacheNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
T pool;
|
||||||
|
|
||||||
|
// Try to find the pool on the cache.
|
||||||
|
for (LinkedListNode<T> node = _pools.First; node != null; node = node.Next)
|
||||||
|
{
|
||||||
|
pool = node.Value;
|
||||||
|
|
||||||
|
if (pool.Address == address)
|
||||||
|
{
|
||||||
|
if (pool.CacheNode != _pools.Last)
|
||||||
|
{
|
||||||
|
_pools.Remove(pool.CacheNode);
|
||||||
|
|
||||||
|
pool.CacheNode = _pools.AddLast(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.CacheTimestamp = _currentTimestamp;
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not found, create a new one.
|
||||||
|
pool = CreatePool(_context, channel, address, maximumId);
|
||||||
|
|
||||||
|
pool.CacheNode = _pools.AddLast(pool);
|
||||||
|
pool.CacheTimestamp = _currentTimestamp;
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the pool.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that the pool belongs to</param>
|
||||||
|
/// <param name="channel">GPU channel that the pool belongs to</param>
|
||||||
|
/// <param name="address">Address of the pool in guest memory</param>
|
||||||
|
/// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
|
||||||
|
protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (T pool in _pools)
|
||||||
|
{
|
||||||
|
pool.Dispose();
|
||||||
|
pool.CacheNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pools.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,27 @@
|
|||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sampler pool.
|
/// Sampler pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class SamplerPool : Pool<Sampler, SamplerDescriptor>
|
class SamplerPool : Pool<Sampler, SamplerDescriptor>, IPool<SamplerPool>
|
||||||
{
|
{
|
||||||
private float _forcedAnisotropy;
|
private float _forcedAnisotropy;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance of the sampler pool.
|
/// Linked list node used on the sampler pool cache.
|
||||||
|
/// </summary>
|
||||||
|
public LinkedListNode<SamplerPool> CacheNode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timestamp used by the sampler pool cache, updated on every use of this sampler pool.
|
||||||
|
/// </summary>
|
||||||
|
public ulong CacheTimestamp { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the sampler pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
||||||
/// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param>
|
/// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param>
|
||||||
|
|||||||
30
Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs
Normal file
30
Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sampler pool cache.
|
||||||
|
/// This can keep multiple sampler pools, and return the current one as needed.
|
||||||
|
/// It is useful for applications that uses multiple sampler pools.
|
||||||
|
/// </summary>
|
||||||
|
class SamplerPoolCache : PoolCache<SamplerPool>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a new instance of the texture pool.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
|
public SamplerPoolCache(GpuContext context) : base(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the sampler pool.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
||||||
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
|
/// <param name="address">Address of the sampler pool in guest memory</param>
|
||||||
|
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
|
||||||
|
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
|
||||||
|
{
|
||||||
|
return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Texture bindings manager.
|
/// Texture bindings manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class TextureBindingsManager : IDisposable
|
class TextureBindingsManager
|
||||||
{
|
{
|
||||||
private const int InitialTextureStateSize = 32;
|
private const int InitialTextureStateSize = 32;
|
||||||
private const int InitialImageStateSize = 8;
|
private const int InitialImageStateSize = 8;
|
||||||
@@ -22,15 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
private readonly bool _isCompute;
|
private readonly bool _isCompute;
|
||||||
|
|
||||||
private SamplerPool _samplerPool;
|
private ulong _texturePoolGpuVa;
|
||||||
|
|
||||||
private SamplerIndex _samplerIndex;
|
|
||||||
|
|
||||||
private ulong _texturePoolAddress;
|
|
||||||
private int _texturePoolMaximumId;
|
private int _texturePoolMaximumId;
|
||||||
|
private TexturePool _texturePool;
|
||||||
|
private ulong _samplerPoolGpuVa;
|
||||||
|
private int _samplerPoolMaximumId;
|
||||||
|
private SamplerIndex _samplerIndex;
|
||||||
|
private SamplerPool _samplerPool;
|
||||||
|
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
private readonly TexturePoolCache _texturePoolCache;
|
private readonly TexturePoolCache _texturePoolCache;
|
||||||
|
private readonly SamplerPoolCache _samplerPoolCache;
|
||||||
|
|
||||||
private TexturePool _cachedTexturePool;
|
private TexturePool _cachedTexturePool;
|
||||||
private SamplerPool _cachedSamplerPool;
|
private SamplerPool _cachedSamplerPool;
|
||||||
@@ -72,14 +74,23 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The GPU context that the texture bindings manager belongs to</param>
|
/// <param name="context">The GPU context that the texture bindings manager belongs to</param>
|
||||||
/// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
|
/// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
|
||||||
/// <param name="poolCache">Texture pools cache used to get texture pools from</param>
|
/// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param>
|
||||||
|
/// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param>
|
||||||
/// <param name="scales">Array where the scales for the currently bound textures are stored</param>
|
/// <param name="scales">Array where the scales for the currently bound textures are stored</param>
|
||||||
/// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
|
/// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
|
||||||
public TextureBindingsManager(GpuContext context, GpuChannel channel, TexturePoolCache poolCache, float[] scales, bool isCompute)
|
public TextureBindingsManager(
|
||||||
|
GpuContext context,
|
||||||
|
GpuChannel channel,
|
||||||
|
TexturePoolCache texturePoolCache,
|
||||||
|
SamplerPoolCache samplerPoolCache,
|
||||||
|
float[] scales,
|
||||||
|
bool isCompute)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
_texturePoolCache = poolCache;
|
_texturePoolCache = texturePoolCache;
|
||||||
|
_samplerPoolCache = samplerPoolCache;
|
||||||
|
|
||||||
_scales = scales;
|
_scales = scales;
|
||||||
_isCompute = isCompute;
|
_isCompute = isCompute;
|
||||||
|
|
||||||
@@ -173,25 +184,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
|
/// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
|
||||||
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
|
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
|
||||||
{
|
{
|
||||||
if (gpuVa != 0)
|
_samplerPoolGpuVa = gpuVa;
|
||||||
{
|
_samplerPoolMaximumId = maximumId;
|
||||||
ulong address = _channel.MemoryManager.Translate(gpuVa);
|
|
||||||
|
|
||||||
if (_samplerPool != null && _samplerPool.Address == address && _samplerPool.MaximumId >= maximumId)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_samplerPool?.Dispose();
|
|
||||||
_samplerPool = new SamplerPool(_context, _channel.MemoryManager.Physical, address, maximumId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_samplerPool?.Dispose();
|
|
||||||
_samplerPool = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_samplerIndex = samplerIndex;
|
_samplerIndex = samplerIndex;
|
||||||
|
_samplerPool = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -201,18 +197,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
|
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
|
||||||
public void SetTexturePool(ulong gpuVa, int maximumId)
|
public void SetTexturePool(ulong gpuVa, int maximumId)
|
||||||
{
|
{
|
||||||
if (gpuVa != 0)
|
_texturePoolGpuVa = gpuVa;
|
||||||
{
|
|
||||||
ulong address = _channel.MemoryManager.Translate(gpuVa);
|
|
||||||
|
|
||||||
_texturePoolAddress = address;
|
|
||||||
_texturePoolMaximumId = maximumId;
|
_texturePoolMaximumId = maximumId;
|
||||||
}
|
_texturePool = null;
|
||||||
else
|
|
||||||
{
|
|
||||||
_texturePoolAddress = 0;
|
|
||||||
_texturePoolMaximumId = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -222,13 +209,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="samplerId">ID of the sampler</param>
|
/// <param name="samplerId">ID of the sampler</param>
|
||||||
public (Texture, Sampler) GetTextureAndSampler(int textureId, int samplerId)
|
public (Texture, Sampler) GetTextureAndSampler(int textureId, int samplerId)
|
||||||
{
|
{
|
||||||
ulong texturePoolAddress = _texturePoolAddress;
|
(TexturePool texturePool, SamplerPool samplerPool) = GetPools();
|
||||||
|
|
||||||
TexturePool texturePool = texturePoolAddress != 0
|
return (texturePool.Get(textureId), samplerPool.Get(samplerId));
|
||||||
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return (texturePool.Get(textureId), _samplerPool.Get(samplerId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -340,13 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
|
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
|
||||||
public bool CommitBindings(ShaderSpecializationState specState)
|
public bool CommitBindings(ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
ulong texturePoolAddress = _texturePoolAddress;
|
(TexturePool texturePool, SamplerPool samplerPool) = GetPools();
|
||||||
|
|
||||||
TexturePool texturePool = texturePoolAddress != 0
|
|
||||||
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
SamplerPool samplerPool = _samplerPool;
|
|
||||||
|
|
||||||
// Check if the texture pool has been modified since bindings were last committed.
|
// Check if the texture pool has been modified since bindings were last committed.
|
||||||
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
|
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
|
||||||
@@ -381,7 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (_isCompute)
|
if (_isCompute)
|
||||||
{
|
{
|
||||||
specStateMatches &= CommitTextureBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
|
specStateMatches &= CommitTextureBindings(texturePool, samplerPool, ShaderStage.Compute, 0, poolModified, specState);
|
||||||
specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
|
specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -390,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
int stageIndex = (int)stage - 1;
|
int stageIndex = (int)stage - 1;
|
||||||
|
|
||||||
specStateMatches &= CommitTextureBindings(texturePool, stage, stageIndex, poolModified, specState);
|
specStateMatches &= CommitTextureBindings(texturePool, samplerPool, stage, stageIndex, poolModified, specState);
|
||||||
specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState);
|
specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,13 +424,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Ensures that the texture bindings are visible to the host GPU.
|
/// Ensures that the texture bindings are visible to the host GPU.
|
||||||
/// Note: this actually performs the binding using the host graphics API.
|
/// Note: this actually performs the binding using the host graphics API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pool">The current texture pool</param>
|
/// <param name="texturePool">The current texture pool</param>
|
||||||
|
/// <param name="samplerPool">The current sampler pool</param>
|
||||||
/// <param name="stage">The shader stage using the textures to be bound</param>
|
/// <param name="stage">The shader stage using the textures to be bound</param>
|
||||||
/// <param name="stageIndex">The stage number of the specified shader stage</param
|
/// <param name="stageIndex">The stage number of the specified shader stage</param
|
||||||
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
|
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
|
||||||
/// <param name="specState">Specialization state for the bound shader</param>
|
/// <param name="specState">Specialization state for the bound shader</param>
|
||||||
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
|
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
|
||||||
private bool CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState)
|
private bool CommitTextureBindings(
|
||||||
|
TexturePool texturePool,
|
||||||
|
SamplerPool samplerPool,
|
||||||
|
ShaderStage stage,
|
||||||
|
int stageIndex,
|
||||||
|
bool poolModified,
|
||||||
|
ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
int textureCount = _textureBindingsCount[stageIndex];
|
int textureCount = _textureBindingsCount[stageIndex];
|
||||||
if (textureCount == 0)
|
if (textureCount == 0)
|
||||||
@@ -461,9 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var samplerPool = _samplerPool;
|
if (texturePool == null)
|
||||||
|
|
||||||
if (pool == null)
|
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
|
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
|
||||||
return true;
|
return true;
|
||||||
@@ -528,7 +510,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
state.TextureHandle = textureId;
|
state.TextureHandle = textureId;
|
||||||
state.SamplerHandle = samplerId;
|
state.SamplerHandle = samplerId;
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture);
|
||||||
|
|
||||||
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
|
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
|
||||||
|
|
||||||
@@ -819,6 +801,54 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the texture and sampler pool for the GPU virtual address that are currently set.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The texture and sampler pools</returns>
|
||||||
|
private (TexturePool, SamplerPool) GetPools()
|
||||||
|
{
|
||||||
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
|
|
||||||
|
TexturePool texturePool = _texturePool;
|
||||||
|
SamplerPool samplerPool = _samplerPool;
|
||||||
|
|
||||||
|
if (texturePool == null)
|
||||||
|
{
|
||||||
|
ulong poolAddress = memoryManager.Translate(_texturePoolGpuVa);
|
||||||
|
|
||||||
|
if (poolAddress != MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId);
|
||||||
|
_texturePool = texturePool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (samplerPool == null)
|
||||||
|
{
|
||||||
|
ulong poolAddress = memoryManager.Translate(_samplerPoolGpuVa);
|
||||||
|
|
||||||
|
if (poolAddress != MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId);
|
||||||
|
_samplerPool = samplerPool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (texturePool, samplerPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forces the texture and sampler pools to be re-loaded from the cache on next use.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This should be called if the memory mappings change, to ensure the correct pools are being used.
|
||||||
|
/// </remarks>
|
||||||
|
public void ReloadPools()
|
||||||
|
{
|
||||||
|
_samplerPool = null;
|
||||||
|
_texturePool = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
|
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -827,13 +857,5 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
Array.Clear(_textureState);
|
Array.Clear(_textureState);
|
||||||
Array.Clear(_imageState);
|
Array.Clear(_imageState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disposes all textures and samplers in the cache.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_samplerPool?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -900,23 +900,25 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
|
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
||||||
/// <param name="tex">The texture information</param>
|
|
||||||
/// <param name="gpuVa">GPU virtual address of the texture</param>
|
/// <param name="gpuVa">GPU virtual address of the texture</param>
|
||||||
/// <param name="bpp">Bytes per pixel</param>
|
/// <param name="bpp">Bytes per pixel</param>
|
||||||
/// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param>
|
/// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param>
|
||||||
|
/// <param name="height">If <paramref name="linear"/> is false, should have the texture height, otherwise ignored</param>
|
||||||
/// <param name="xCount">Number of pixels to be copied per line</param>
|
/// <param name="xCount">Number of pixels to be copied per line</param>
|
||||||
/// <param name="yCount">Number of lines to be copied</param>
|
/// <param name="yCount">Number of lines to be copied</param>
|
||||||
/// <param name="linear">True if the texture has a linear layout, false otherwise</param>
|
/// <param name="linear">True if the texture has a linear layout, false otherwise</param>
|
||||||
|
/// <param name="memoryLayout">If <paramref name="linear"/> is false, should have the memory layout, otherwise ignored</param>
|
||||||
/// <returns>A matching texture, or null if there is no match</returns>
|
/// <returns>A matching texture, or null if there is no match</returns>
|
||||||
public Texture FindTexture(
|
public Texture FindTexture(
|
||||||
MemoryManager memoryManager,
|
MemoryManager memoryManager,
|
||||||
DmaTexture tex,
|
|
||||||
ulong gpuVa,
|
ulong gpuVa,
|
||||||
int bpp,
|
int bpp,
|
||||||
int stride,
|
int stride,
|
||||||
|
int height,
|
||||||
int xCount,
|
int xCount,
|
||||||
int yCount,
|
int yCount,
|
||||||
bool linear)
|
bool linear,
|
||||||
|
MemoryLayout memoryLayout)
|
||||||
{
|
{
|
||||||
ulong address = memoryManager.Translate(gpuVa);
|
ulong address = memoryManager.Translate(gpuVa);
|
||||||
|
|
||||||
@@ -945,7 +947,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
// Size is not available for linear textures. Use the stride and end of the copy region instead.
|
// Size is not available for linear textures. Use the stride and end of the copy region instead.
|
||||||
|
|
||||||
match = texture.Info.IsLinear && texture.Info.Stride == stride && tex.RegionY + yCount <= texture.Info.Height;
|
match = texture.Info.IsLinear && texture.Info.Stride == stride && yCount == texture.Info.Height;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -953,10 +955,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison.
|
// Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison.
|
||||||
// Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size.
|
// Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size.
|
||||||
|
|
||||||
bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && tex.Height == texture.Info.Height;
|
bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && height == texture.Info.Height;
|
||||||
bool formatMatch = !texture.Info.IsLinear &&
|
bool formatMatch = !texture.Info.IsLinear &&
|
||||||
texture.Info.GobBlocksInY == tex.MemoryLayout.UnpackGobBlocksInY() &&
|
texture.Info.GobBlocksInY == memoryLayout.UnpackGobBlocksInY() &&
|
||||||
texture.Info.GobBlocksInZ == tex.MemoryLayout.UnpackGobBlocksInZ();
|
texture.Info.GobBlocksInZ == memoryLayout.UnpackGobBlocksInZ();
|
||||||
|
|
||||||
match = sizeMatch && formatMatch;
|
match = sizeMatch && formatMatch;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly TextureBindingsManager _cpBindingsManager;
|
private readonly TextureBindingsManager _cpBindingsManager;
|
||||||
private readonly TextureBindingsManager _gpBindingsManager;
|
private readonly TextureBindingsManager _gpBindingsManager;
|
||||||
private readonly TexturePoolCache _texturePoolCache;
|
private readonly TexturePoolCache _texturePoolCache;
|
||||||
|
private readonly SamplerPoolCache _samplerPoolCache;
|
||||||
|
|
||||||
private readonly Texture[] _rtColors;
|
private readonly Texture[] _rtColors;
|
||||||
private readonly ITexture[] _rtHostColors;
|
private readonly ITexture[] _rtHostColors;
|
||||||
@@ -41,13 +42,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
|
||||||
TexturePoolCache texturePoolCache = new TexturePoolCache(context);
|
TexturePoolCache texturePoolCache = new TexturePoolCache(context);
|
||||||
|
SamplerPoolCache samplerPoolCache = new SamplerPoolCache(context);
|
||||||
|
|
||||||
float[] scales = new float[64];
|
float[] scales = new float[64];
|
||||||
new Span<float>(scales).Fill(1f);
|
new Span<float>(scales).Fill(1f);
|
||||||
|
|
||||||
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: true);
|
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: true);
|
||||||
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: false);
|
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: false);
|
||||||
_texturePoolCache = texturePoolCache;
|
_texturePoolCache = texturePoolCache;
|
||||||
|
_samplerPoolCache = samplerPoolCache;
|
||||||
|
|
||||||
_rtColors = new Texture[Constants.TotalRenderTargets];
|
_rtColors = new Texture[Constants.TotalRenderTargets];
|
||||||
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
||||||
@@ -368,6 +371,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// we must rebind everything.
|
// we must rebind everything.
|
||||||
// Since compute work happens less often, we always do that
|
// Since compute work happens less often, we always do that
|
||||||
// before and after the compute dispatch.
|
// before and after the compute dispatch.
|
||||||
|
|
||||||
|
_texturePoolCache.Tick();
|
||||||
|
_samplerPoolCache.Tick();
|
||||||
|
|
||||||
_cpBindingsManager.Rebind();
|
_cpBindingsManager.Rebind();
|
||||||
bool result = _cpBindingsManager.CommitBindings(specState);
|
bool result = _cpBindingsManager.CommitBindings(specState);
|
||||||
_gpBindingsManager.Rebind();
|
_gpBindingsManager.Rebind();
|
||||||
@@ -382,6 +389,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
|
/// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
|
||||||
public bool CommitGraphicsBindings(ShaderSpecializationState specState)
|
public bool CommitGraphicsBindings(ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
|
_texturePoolCache.Tick();
|
||||||
|
_samplerPoolCache.Tick();
|
||||||
|
|
||||||
bool result = _gpBindingsManager.CommitBindings(specState);
|
bool result = _gpBindingsManager.CommitBindings(specState);
|
||||||
|
|
||||||
UpdateRenderTargets();
|
UpdateRenderTargets();
|
||||||
@@ -501,6 +511,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forces the texture and sampler pools to be re-loaded from the cache on next use.
|
||||||
|
/// </summary>
|
||||||
|
public void ReloadPools()
|
||||||
|
{
|
||||||
|
_cpBindingsManager.ReloadPools();
|
||||||
|
_gpBindingsManager.ReloadPools();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Forces all textures, samplers, images and render targets to be rebound the next time
|
/// Forces all textures, samplers, images and render targets to be rebound the next time
|
||||||
/// CommitGraphicsBindings is called.
|
/// CommitGraphicsBindings is called.
|
||||||
@@ -523,8 +542,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_cpBindingsManager.Dispose();
|
// Textures are owned by the texture cache, so we shouldn't dispose the texture pool cache.
|
||||||
_gpBindingsManager.Dispose();
|
_samplerPoolCache.Dispose();
|
||||||
|
|
||||||
for (int i = 0; i < _rtColors.Length; i++)
|
for (int i = 0; i < _rtColors.Length; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,19 +10,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Texture pool.
|
/// Texture pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class TexturePool : Pool<Texture, TextureDescriptor>
|
class TexturePool : Pool<Texture, TextureDescriptor>, IPool<TexturePool>
|
||||||
{
|
{
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
|
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
|
||||||
private TextureDescriptor _defaultDescriptor;
|
private TextureDescriptor _defaultDescriptor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Intrusive linked list node used on the texture pool cache.
|
/// Linked list node used on the texture pool cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LinkedListNode<TexturePool> CacheNode { get; set; }
|
public LinkedListNode<TexturePool> CacheNode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance of the texture pool.
|
/// Timestamp used by the texture pool cache, updated on every use of this texture pool.
|
||||||
|
/// </summary>
|
||||||
|
public ulong CacheTimestamp { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the texture pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -8,69 +5,26 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// This can keep multiple texture pools, and return the current one as needed.
|
/// This can keep multiple texture pools, and return the current one as needed.
|
||||||
/// It is useful for applications that uses multiple texture pools.
|
/// It is useful for applications that uses multiple texture pools.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class TexturePoolCache
|
class TexturePoolCache : PoolCache<TexturePool>
|
||||||
{
|
{
|
||||||
private const int MaxCapacity = 4;
|
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
|
||||||
private readonly LinkedList<TexturePool> _pools;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance of the texture pool.
|
/// Constructs a new instance of the texture pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
public TexturePoolCache(GpuContext context)
|
public TexturePoolCache(GpuContext context) : base(context)
|
||||||
{
|
{
|
||||||
_context = context;
|
|
||||||
_pools = new LinkedList<TexturePool>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds a cache texture pool, or creates a new one if not found.
|
/// Creates a new instance of the texture pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel that the texture pool cache belongs to</param>
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
/// <param name="address">Start address of the texture pool</param>
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
/// <param name="maximumId">Maximum ID of the texture pool</param>
|
/// <param name="address">Address of the texture pool in guest memory</param>
|
||||||
/// <returns>The found or newly created texture pool</returns>
|
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
||||||
public TexturePool FindOrCreate(GpuChannel channel, ulong address, int maximumId)
|
protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
|
||||||
{
|
{
|
||||||
TexturePool pool;
|
return new TexturePool(context, channel, address, maximumId);
|
||||||
|
|
||||||
// First we try to find the pool.
|
|
||||||
for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next)
|
|
||||||
{
|
|
||||||
pool = node.Value;
|
|
||||||
|
|
||||||
if (pool.Address == address)
|
|
||||||
{
|
|
||||||
if (pool.CacheNode != _pools.Last)
|
|
||||||
{
|
|
||||||
_pools.Remove(pool.CacheNode);
|
|
||||||
|
|
||||||
pool.CacheNode = _pools.AddLast(pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not found, create a new one.
|
|
||||||
pool = new TexturePool(_context, channel, address, maximumId);
|
|
||||||
|
|
||||||
pool.CacheNode = _pools.AddLast(pool);
|
|
||||||
|
|
||||||
if (_pools.Count > MaxCapacity)
|
|
||||||
{
|
|
||||||
TexturePool oldestPool = _pools.First.Value;
|
|
||||||
|
|
||||||
_pools.RemoveFirst();
|
|
||||||
|
|
||||||
oldestPool.Dispose();
|
|
||||||
|
|
||||||
oldestPool.CacheNode = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pool;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user