Compare commits

...

11 Commits

Author SHA1 Message Date
6ef8946169 Avalonia: Fix gamescope once and for all (#6301)
Enable input focus proxy, makes WM_TAKE_FOCUS capability to be exposed.

This fix menu on gamescope, for real this time....

Signed-off-by: Mary Guillemard <mary@mary.zone>
2024-02-19 21:18:46 +01:00
42340fc743 LightningJit: Add a limit on the number of instructions per function for Arm64 (#6328) 2024-02-17 17:30:54 -03:00
103e7cb021 hid: Stub SetTouchScreenResolution (#6322)
* hid: Implement SetTouchScreenResolution

* Fix Tomb Raider I-III Remastered from crashing without enabling Ignore Missing Services

* PR Feedback: Update Comments
2024-02-17 14:49:50 -03:00
31ed061bea Vulkan: Improve texture barrier usage, timing and batching (#6240)
* WIP barrier batch

* Add store op to image usage barrier

* Dispose the barrier batch

* Fix encoding?

* Handle read and write on the load op barrier.

Load op consumes read accesses but does not add one, as the only other operation that can read is another load.

* Simplify null check

* Insert barriers on program change in case stale bindings are reintroduced

* Not sure how I messed this one up

* Improve location of bindings barrier update

This is also important for emergency deferred clear

* Update src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs

Co-authored-by: Mary Guillemard <thog@protonmail.com>

---------

Co-authored-by: Mary Guillemard <thog@protonmail.com>
2024-02-17 00:21:37 -03:00
4218311e6a Vulkan: Use push descriptors for uniform bindings when possible (#6154)
* Fix Push Descriptors

* Use push descriptor templates

* Use reserved bindings

* Formatting

* Disable when using MVK

("my heart will go on" starts playing as thousands of mac users shed a tear in unison)

* Introduce limit on push descriptor binding number

The bitmask used for updating push descriptors is ulong, so only 64 bindings can be tracked for now.

* Address feedback

* Fix logic for binding rejection

Should only offset limit when reserved bindings are less than the requested one.

* Workaround pascal and older nv bug

* Add GPU number detection for nvidia

* Only do workaround if it's valid to do so.
2024-02-16 21:41:30 -03:00
e37735ed26 Implement X8Z24 texture format (#6315) 2024-02-15 19:06:26 -03:00
74a18b7c18 Fix PermissionLocked check on UnmapProcessCodeMemory (#6314) 2024-02-15 16:16:01 -03:00
74fe814329 Remove Vulkan SubgroupSizeControl enablement code (#6317) 2024-02-15 16:04:30 -03:00
d1a093e5ca Stub VSMS related ioctls (#6313)
* Stub VSMS related ioctls

* Clean up usings
2024-02-15 19:48:22 +01:00
dfb14a5607 Updaters: Fix ARM Linux Updater (#6316)
* Remove Arch Checks

* Fix ARM Linux updater
2024-02-15 10:41:43 +01:00
jcm
904a5ffcb4 Handle exceptions when checking user data directory for symlink (#6304)
Co-authored-by: jcm <butt@butts.com>
2024-02-12 00:10:21 +01:00
48 changed files with 973 additions and 283 deletions

View File

@ -333,8 +333,6 @@
"DialogUpdaterAddingFilesMessage": "Adding New Update...", "DialogUpdaterAddingFilesMessage": "Adding New Update...",
"DialogUpdaterCompleteMessage": "Update Complete!", "DialogUpdaterCompleteMessage": "Update Complete!",
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?", "DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?",
"DialogUpdaterArchNotSupportedMessage": "You are not running a supported system architecture!",
"DialogUpdaterArchNotSupportedSubMessage": "(Only x64 systems are supported!)",
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!", "DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!", "DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
"DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!", "DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!",

View File

@ -68,7 +68,8 @@ namespace Ryujinx.Modules
} }
else if (OperatingSystem.IsLinux()) else if (OperatingSystem.IsLinux())
{ {
_platformExt = "linux_x64.tar.gz"; var arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64";
_platformExt = $"linux_{arch}.tar.gz";
} }
Version newVersion; Version newVersion;
@ -637,20 +638,6 @@ namespace Ryujinx.Modules
public static bool CanUpdate(bool showWarnings) public static bool CanUpdate(bool showWarnings)
{ {
#if !DISABLE_UPDATER #if !DISABLE_UPDATER
if (RuntimeInformation.OSArchitecture != Architecture.X64 && !OperatingSystem.IsMacOS())
{
if (showWarnings)
{
Dispatcher.UIThread.InvokeAsync(() =>
ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage])
);
}
return false;
}
if (!NetworkInterface.GetIsNetworkAvailable()) if (!NetworkInterface.GetIsNetworkAvailable())
{ {
if (showWarnings) if (showWarnings)

View File

@ -59,6 +59,7 @@ namespace Ryujinx.Ava
{ {
EnableMultiTouch = true, EnableMultiTouch = true,
EnableIme = true, EnableIme = true,
EnableInputFocusProxy = true,
RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software }, RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software },
}) })
.With(new Win32PlatformOptions .With(new Win32PlatformOptions

View File

@ -233,8 +233,15 @@ namespace Ryujinx.Common.Configuration
// Should be removed, when the existence of the old directory isn't checked anymore. // Should be removed, when the existence of the old directory isn't checked anymore.
private static bool IsPathSymlink(string path) private static bool IsPathSymlink(string path)
{ {
FileAttributes attributes = File.GetAttributes(path); try
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint; {
FileAttributes attributes = File.GetAttributes(path);
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
}
catch
{
return false;
}
} }
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]

View File

@ -8,7 +8,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
{ {
static class Decoder static class Decoder
{ {
private const int MaxInstructionsPerBlock = 1000; private const int MaxInstructionsPerFunction = 10000;
private const uint NzcvFlags = 0xfu << 28; private const uint NzcvFlags = 0xfu << 28;
private const uint CFlag = 0x1u << 29; private const uint CFlag = 0x1u << 29;
@ -22,10 +22,11 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
bool hasHostCall = false; bool hasHostCall = false;
bool hasMemoryInstruction = false; bool hasMemoryInstruction = false;
int totalInsts = 0;
while (true) while (true)
{ {
Block block = Decode(cpuPreset, memoryManager, address, ref useMask, ref hasHostCall, ref hasMemoryInstruction); Block block = Decode(cpuPreset, memoryManager, address, ref totalInsts, ref useMask, ref hasHostCall, ref hasMemoryInstruction);
if (!block.IsTruncated && TryGetBranchTarget(block, out ulong targetAddress)) if (!block.IsTruncated && TryGetBranchTarget(block, out ulong targetAddress))
{ {
@ -230,6 +231,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
CpuPreset cpuPreset, CpuPreset cpuPreset,
IMemoryManager memoryManager, IMemoryManager memoryManager,
ulong address, ulong address,
ref int totalInsts,
ref RegisterMask useMask, ref RegisterMask useMask,
ref bool hasHostCall, ref bool hasHostCall,
ref bool hasMemoryInstruction) ref bool hasMemoryInstruction)
@ -272,7 +274,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
uint tempGprUseMask = gprUseMask | instGprReadMask | instGprWriteMask; uint tempGprUseMask = gprUseMask | instGprReadMask | instGprWriteMask;
if (CalculateAvailableTemps(tempGprUseMask) < CalculateRequiredGprTemps(tempGprUseMask) || insts.Count >= MaxInstructionsPerBlock) if (CalculateAvailableTemps(tempGprUseMask) < CalculateRequiredGprTemps(tempGprUseMask) || totalInsts++ >= MaxInstructionsPerFunction)
{ {
isTruncated = true; isTruncated = true;
address -= 4UL; address -= 4UL;

View File

@ -148,6 +148,7 @@ namespace Ryujinx.Graphics.GAL
B8G8R8A8Unorm, B8G8R8A8Unorm,
B8G8R8A8Srgb, B8G8R8A8Srgb,
B10G10R10A2Unorm, B10G10R10A2Unorm,
X8UintD24Unorm,
} }
public static class FormatExtensions public static class FormatExtensions
@ -269,6 +270,7 @@ namespace Ryujinx.Graphics.GAL
case Format.D16Unorm: case Format.D16Unorm:
return 2; return 2;
case Format.S8UintD24Unorm: case Format.S8UintD24Unorm:
case Format.X8UintD24Unorm:
case Format.D32Float: case Format.D32Float:
case Format.D24UnormS8Uint: case Format.D24UnormS8Uint:
return 4; return 4;
@ -349,6 +351,7 @@ namespace Ryujinx.Graphics.GAL
case Format.D16Unorm: case Format.D16Unorm:
case Format.D24UnormS8Uint: case Format.D24UnormS8Uint:
case Format.S8UintD24Unorm: case Format.S8UintD24Unorm:
case Format.X8UintD24Unorm:
case Format.D32Float: case Format.D32Float:
case Format.D32FloatS8Uint: case Format.D32FloatS8Uint:
return true; return true;
@ -633,6 +636,7 @@ namespace Ryujinx.Graphics.GAL
case Format.D16Unorm: case Format.D16Unorm:
case Format.D24UnormS8Uint: case Format.D24UnormS8Uint:
case Format.S8UintD24Unorm: case Format.S8UintD24Unorm:
case Format.X8UintD24Unorm:
case Format.D32Float: case Format.D32Float:
case Format.D32FloatS8Uint: case Format.D32FloatS8Uint:
case Format.S8Uint: case Format.S8Uint:

View File

@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL
void SetIndexBuffer(BufferRange buffer, IndexType type); void SetIndexBuffer(BufferRange buffer, IndexType type);
void SetImage(int binding, ITexture texture, Format imageFormat); void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat);
void SetLineParameters(float width, bool smooth); void SetLineParameters(float width, bool smooth);

View File

@ -1,17 +1,20 @@
using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources; using Ryujinx.Graphics.GAL.Multithreading.Resources;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{ {
struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand> struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand>
{ {
public readonly CommandType CommandType => CommandType.SetImage; public readonly CommandType CommandType => CommandType.SetImage;
private ShaderStage _stage;
private int _binding; private int _binding;
private TableRef<ITexture> _texture; private TableRef<ITexture> _texture;
private Format _imageFormat; private Format _imageFormat;
public void Set(int binding, TableRef<ITexture> texture, Format imageFormat) public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture, Format imageFormat)
{ {
_stage = stage;
_binding = binding; _binding = binding;
_texture = texture; _texture = texture;
_imageFormat = imageFormat; _imageFormat = imageFormat;
@ -19,7 +22,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
renderer.Pipeline.SetImage(command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat); renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat);
} }
} }
} }

View File

@ -177,9 +177,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetImage(int binding, ITexture texture, Format imageFormat) public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
{ {
_renderer.New<SetImageCommand>().Set(binding, Ref(texture), imageFormat); _renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture), imageFormat);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }

View File

@ -8,13 +8,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
/// </summary> /// </summary>
enum ZetaFormat enum ZetaFormat
{ {
D32Float = 0xa, Zf32 = 0xa,
D16Unorm = 0x13, Z16 = 0x13,
D24UnormS8Uint = 0x14, Z24S8 = 0x14,
D24Unorm = 0x15, X8Z24 = 0x15,
S8UintD24Unorm = 0x16, S8Z24 = 0x16,
S8Uint = 0x17, S8Uint = 0x17,
D32FloatS8Uint = 0x19, Zf32X24S8 = 0x19,
} }
static class ZetaFormatConverter static class ZetaFormatConverter
@ -29,14 +29,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
return format switch return format switch
{ {
#pragma warning disable IDE0055 // Disable formatting #pragma warning disable IDE0055 // Disable formatting
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1), ZetaFormat.Zf32 => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1), ZetaFormat.Z16 => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2), ZetaFormat.Z24S8 => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1), ZetaFormat.X8Z24 => new FormatInfo(Format.X8UintD24Unorm, 1, 1, 4, 1),
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2), ZetaFormat.S8Z24 => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2),
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1), ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2), ZetaFormat.Zf32X24S8 => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
_ => FormatInfo.Default, _ => FormatInfo.Default,
#pragma warning restore IDE0055 #pragma warning restore IDE0055
}; };
} }

View File

@ -185,6 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Image
G24R8RUintGUnormBUnormAUnorm = G24R8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a0e G24R8RUintGUnormBUnormAUnorm = G24R8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a0e
Z24S8RUintGUnormBUnormAUnorm = Z24S8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a29 Z24S8RUintGUnormBUnormAUnorm = Z24S8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a29
Z24S8RUintGUnormBUintAUint = Z24S8 | RUint | GUnorm | BUint | AUint, // 0x48a29 Z24S8RUintGUnormBUintAUint = Z24S8 | RUint | GUnorm | BUint | AUint, // 0x48a29
X8Z24RUnormGUintBUintAUint = X8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912a
S8Z24RUnormGUintBUintAUint = S8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912b S8Z24RUnormGUintBUintAUint = S8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912b
R32B24G8RFloatGUintBUnormAUnorm = R32B24G8 | RFloat | GUint | BUnorm | AUnorm, // 0x25385 R32B24G8RFloatGUintBUnormAUnorm = R32B24G8 | RFloat | GUint | BUnorm | AUnorm, // 0x25385
Zf32X24S8RFloatGUintBUnormAUnorm = Zf32X24S8 | RFloat | GUint | BUnorm | AUnorm, // 0x253b0 Zf32X24S8RFloatGUintBUnormAUnorm = Zf32X24S8 | RFloat | GUint | BUnorm | AUnorm, // 0x253b0
@ -410,6 +411,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ TextureFormat.G24R8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) }, { TextureFormat.G24R8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
{ TextureFormat.Z24S8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) }, { TextureFormat.Z24S8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
{ TextureFormat.Z24S8RUintGUnormBUintAUint, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) }, { TextureFormat.Z24S8RUintGUnormBUintAUint, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
{ TextureFormat.X8Z24RUnormGUintBUintAUint, new FormatInfo(Format.X8UintD24Unorm, 1, 1, 4, 2) },
{ TextureFormat.S8Z24RUnormGUintBUintAUint, new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2) }, { TextureFormat.S8Z24RUnormGUintBUintAUint, new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2) },
{ TextureFormat.R32B24G8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) }, { TextureFormat.R32B24G8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
{ TextureFormat.Zf32X24S8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) }, { TextureFormat.Zf32X24S8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },

View File

@ -634,7 +634,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.Texture = hostTextureRebind; state.Texture = hostTextureRebind;
state.ImageFormat = format; state.ImageFormat = format;
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTextureRebind, format); _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind, format);
} }
continue; continue;
@ -692,7 +692,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.ImageFormat = format; state.ImageFormat = format;
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format); _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture, format);
} }
state.CachedTexture = texture; state.CachedTexture = texture;

View File

@ -242,7 +242,8 @@ namespace Ryujinx.Graphics.Gpu.Image
return TextureMatchQuality.FormatAlias; return TextureMatchQuality.FormatAlias;
} }
else if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint || else if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
lhs.FormatInfo.Format == Format.S8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm) lhs.FormatInfo.Format == Format.S8UintD24Unorm ||
lhs.FormatInfo.Format == Format.X8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
{ {
return TextureMatchQuality.FormatAlias; return TextureMatchQuality.FormatAlias;
} }

View File

@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (binding.IsImage) if (binding.IsImage)
{ {
_context.Renderer.Pipeline.SetImage(binding.BindingInfo.Binding, binding.Texture, binding.Format); _context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture, binding.Format);
} }
else else
{ {

View File

@ -68,6 +68,7 @@ namespace Ryujinx.Graphics.OpenGL
Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte)); Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte));
Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort)); Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort));
Add(Format.S8UintD24Unorm, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248)); Add(Format.S8UintD24Unorm, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
Add(Format.X8UintD24Unorm, new FormatInfo(1, false, false, All.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt));
Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float)); Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float));
Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248)); Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev)); Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev));
@ -224,5 +225,17 @@ namespace Ryujinx.Graphics.OpenGL
{ {
return _tableImage[(int)format]; return _tableImage[(int)format];
} }
public static bool IsPackedDepthStencil(Format format)
{
return format == Format.D24UnormS8Uint ||
format == Format.D32FloatS8Uint ||
format == Format.S8UintD24Unorm;
}
public static bool IsDepthOnly(Format format)
{
return format == Format.D16Unorm || format == Format.D32Float || format == Format.X8UintD24Unorm;
}
} }
} }

View File

@ -119,11 +119,11 @@ namespace Ryujinx.Graphics.OpenGL
private static FramebufferAttachment GetAttachment(Format format) private static FramebufferAttachment GetAttachment(Format format)
{ {
if (IsPackedDepthStencilFormat(format)) if (FormatTable.IsPackedDepthStencil(format))
{ {
return FramebufferAttachment.DepthStencilAttachment; return FramebufferAttachment.DepthStencilAttachment;
} }
else if (IsDepthOnlyFormat(format)) else if (FormatTable.IsDepthOnly(format))
{ {
return FramebufferAttachment.DepthAttachment; return FramebufferAttachment.DepthAttachment;
} }
@ -133,18 +133,6 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
private static bool IsPackedDepthStencilFormat(Format format)
{
return format == Format.D24UnormS8Uint ||
format == Format.D32FloatS8Uint ||
format == Format.S8UintD24Unorm;
}
private static bool IsDepthOnlyFormat(Format format)
{
return format == Format.D16Unorm || format == Format.D32Float;
}
public int GetColorLayerCount(int index) public int GetColorLayerCount(int index)
{ {
return _colors[index]?.Info.GetDepthOrLayers() ?? 0; return _colors[index]?.Info.GetDepthOrLayers() ?? 0;

View File

@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
return FramebufferAttachment.DepthStencilAttachment; return FramebufferAttachment.DepthStencilAttachment;
} }
else if (IsDepthOnly(format)) else if (FormatTable.IsDepthOnly(format))
{ {
return FramebufferAttachment.DepthAttachment; return FramebufferAttachment.DepthAttachment;
} }
@ -324,11 +324,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
private static ClearBufferMask GetMask(Format format) private static ClearBufferMask GetMask(Format format)
{ {
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint || format == Format.S8UintD24Unorm) if (FormatTable.IsPackedDepthStencil(format))
{ {
return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit; return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
} }
else if (IsDepthOnly(format)) else if (FormatTable.IsDepthOnly(format))
{ {
return ClearBufferMask.DepthBufferBit; return ClearBufferMask.DepthBufferBit;
} }
@ -342,11 +342,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
} }
} }
private static bool IsDepthOnly(Format format)
{
return format == Format.D16Unorm || format == Format.D32Float;
}
public TextureView BgraSwap(TextureView from) public TextureView BgraSwap(TextureView from)
{ {
TextureView to = (TextureView)_renderer.CreateTexture(from.Info); TextureView to = (TextureView)_renderer.CreateTexture(from.Info);

View File

@ -935,7 +935,7 @@ namespace Ryujinx.Graphics.OpenGL
SetFrontFace(_frontFace = frontFace.Convert()); SetFrontFace(_frontFace = frontFace.Convert());
} }
public void SetImage(int binding, ITexture texture, Format imageFormat) public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
{ {
if ((uint)binding < SavedImages) if ((uint)binding < SavedImages)
{ {

View File

@ -0,0 +1,225 @@
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Vulkan
{
internal class BarrierBatch : IDisposable
{
private const int MaxBarriersPerCall = 16;
private readonly VulkanRenderer _gd;
private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
private readonly List<BarrierWithStageFlags<MemoryBarrier>> _memoryBarriers = new();
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier>> _bufferBarriers = new();
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier>> _imageBarriers = new();
private int _queuedBarrierCount;
public BarrierBatch(VulkanRenderer gd)
{
_gd = gd;
}
private readonly record struct StageFlags : IEquatable<StageFlags>
{
public readonly PipelineStageFlags Source;
public readonly PipelineStageFlags Dest;
public StageFlags(PipelineStageFlags source, PipelineStageFlags dest)
{
Source = source;
Dest = dest;
}
}
private readonly struct BarrierWithStageFlags<T> where T : unmanaged
{
public readonly StageFlags Flags;
public readonly T Barrier;
public BarrierWithStageFlags(StageFlags flags, T barrier)
{
Flags = flags;
Barrier = barrier;
}
public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier)
{
Flags = new StageFlags(srcStageFlags, dstStageFlags);
Barrier = barrier;
}
}
private void QueueBarrier<T>(List<BarrierWithStageFlags<T>> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
{
list.Add(new BarrierWithStageFlags<T>(srcStageFlags, dstStageFlags, barrier));
_queuedBarrierCount++;
}
public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags);
}
public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags);
}
public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags);
}
public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass)
{
while (_queuedBarrierCount > 0)
{
int memoryCount = 0;
int bufferCount = 0;
int imageCount = 0;
bool hasBarrier = false;
StageFlags flags = default;
static void AddBarriers<T>(
Span<T> target,
ref int queuedBarrierCount,
ref bool hasBarrier,
ref StageFlags flags,
ref int count,
List<BarrierWithStageFlags<T>> list) where T : unmanaged
{
int firstMatch = -1;
for (int i = 0; i < list.Count; i++)
{
BarrierWithStageFlags<T> barrier = list[i];
if (!hasBarrier)
{
flags = barrier.Flags;
hasBarrier = true;
target[count++] = barrier.Barrier;
queuedBarrierCount--;
firstMatch = i;
if (count >= target.Length)
{
break;
}
}
else
{
if (flags.Equals(barrier.Flags))
{
target[count++] = barrier.Barrier;
queuedBarrierCount--;
if (firstMatch == -1)
{
firstMatch = i;
}
if (count >= target.Length)
{
break;
}
}
else
{
// Delete consumed barriers from the first match to the current non-match.
if (firstMatch != -1)
{
int deleteCount = i - firstMatch;
list.RemoveRange(firstMatch, deleteCount);
i -= deleteCount;
firstMatch = -1;
}
}
}
}
if (firstMatch == 0)
{
list.Clear();
}
else if (firstMatch != -1)
{
int deleteCount = list.Count - firstMatch;
list.RemoveRange(firstMatch, deleteCount);
}
}
if (insideRenderPass)
{
// Image barriers queued in the batch are meant to be globally scoped,
// but inside a render pass they're scoped to just the range of the render pass.
// On MoltenVK, we just break the rules and always use image barrier.
// On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier.
// TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done,
// notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future.
if (!_gd.IsMoltenVk)
{
foreach (var barrier in _imageBarriers)
{
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>(
barrier.Flags,
new MemoryBarrier()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = barrier.Barrier.SrcAccessMask,
DstAccessMask = barrier.Barrier.DstAccessMask
}));
}
_imageBarriers.Clear();
}
}
AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
if (hasBarrier)
{
PipelineStageFlags srcStageFlags = flags.Source;
if (insideRenderPass)
{
// Inside a render pass, barrier stages can only be from rasterization.
srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
}
_gd.Api.CmdPipelineBarrier(
cb,
srcStageFlags,
flags.Dest,
0,
(uint)memoryCount,
_memoryBarrierBatch.Pointer,
(uint)bufferCount,
_bufferBarrierBatch.Pointer,
(uint)imageCount,
_imageBarrierBatch.Pointer);
}
}
}
public void Dispose()
{
_memoryBarrierBatch.Dispose();
_bufferBarrierBatch.Dispose();
_imageBarrierBatch.Dispose();
}
}
}

View File

@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages; public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
public const int MaxPushDescriptorBinding = 64;
public const ulong SparseBufferAlignment = 0x10000; public const ulong SparseBufferAlignment = 0x10000;
} }

View File

@ -1,19 +1,32 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
class DescriptorSetTemplate : IDisposable class DescriptorSetTemplate : IDisposable
{ {
/// <summary>
/// Renderdoc seems to crash when doing a templated uniform update with count > 1 on a push descriptor.
/// When this is true, consecutive buffers are always updated individually.
/// </summary>
private const bool RenderdocPushCountBug = true;
private readonly VulkanRenderer _gd; private readonly VulkanRenderer _gd;
private readonly Device _device; private readonly Device _device;
public readonly DescriptorUpdateTemplate Template; public readonly DescriptorUpdateTemplate Template;
public readonly int Size; public readonly int Size;
public unsafe DescriptorSetTemplate(VulkanRenderer gd, Device device, ResourceBindingSegment[] segments, PipelineLayoutCacheEntry plce, PipelineBindPoint pbp, int setIndex) public unsafe DescriptorSetTemplate(
VulkanRenderer gd,
Device device,
ResourceBindingSegment[] segments,
PipelineLayoutCacheEntry plce,
PipelineBindPoint pbp,
int setIndex)
{ {
_gd = gd; _gd = gd;
_device = device; _device = device;
@ -137,6 +150,93 @@ namespace Ryujinx.Graphics.Vulkan
Template = result; Template = result;
} }
public unsafe DescriptorSetTemplate(
VulkanRenderer gd,
Device device,
ResourceDescriptorCollection descriptors,
long updateMask,
PipelineLayoutCacheEntry plce,
PipelineBindPoint pbp,
int setIndex)
{
_gd = gd;
_device = device;
// Create a template from the set usages. Assumes the descriptor set is updated in segment order then binding order.
int segmentCount = BitOperations.PopCount((ulong)updateMask);
DescriptorUpdateTemplateEntry* entries = stackalloc DescriptorUpdateTemplateEntry[segmentCount];
int entry = 0;
nuint structureOffset = 0;
void AddBinding(int binding, int count)
{
entries[entry++] = new DescriptorUpdateTemplateEntry()
{
DescriptorType = DescriptorType.UniformBuffer,
DstBinding = (uint)binding,
DescriptorCount = (uint)count,
Offset = structureOffset,
Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>()
};
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
}
int startBinding = 0;
int bindingCount = 0;
foreach (ResourceDescriptor descriptor in descriptors.Descriptors)
{
for (int i = 0; i < descriptor.Count; i++)
{
int binding = descriptor.Binding + i;
if ((updateMask & (1L << binding)) != 0)
{
if (bindingCount > 0 && (RenderdocPushCountBug || startBinding + bindingCount != binding))
{
AddBinding(startBinding, bindingCount);
bindingCount = 0;
}
if (bindingCount == 0)
{
startBinding = binding;
}
bindingCount++;
}
}
}
if (bindingCount > 0)
{
AddBinding(startBinding, bindingCount);
}
Size = (int)structureOffset;
var info = new DescriptorUpdateTemplateCreateInfo()
{
SType = StructureType.DescriptorUpdateTemplateCreateInfo,
DescriptorUpdateEntryCount = (uint)entry,
PDescriptorUpdateEntries = entries,
TemplateType = DescriptorUpdateTemplateType.PushDescriptorsKhr,
DescriptorSetLayout = plce.DescriptorSetLayouts[setIndex],
PipelineBindPoint = pbp,
PipelineLayout = plce.PipelineLayout,
Set = (uint)setIndex,
};
DescriptorUpdateTemplate result;
gd.Api.CreateDescriptorUpdateTemplate(device, &info, null, &result).ThrowOnError();
Template = result;
}
public unsafe void Dispose() public unsafe void Dispose()
{ {
_gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null); _gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null);

View File

@ -52,11 +52,23 @@ namespace Ryujinx.Graphics.Vulkan
return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, template.Size)); return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, template.Size));
} }
public DescriptorSetTemplateWriter Begin(int maxSize)
{
EnsureSize(maxSize);
return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, maxSize));
}
public void Commit(VulkanRenderer gd, Device device, DescriptorSet set) public void Commit(VulkanRenderer gd, Device device, DescriptorSet set)
{ {
gd.Api.UpdateDescriptorSetWithTemplate(device, set, _activeTemplate.Template, _data.Pointer); gd.Api.UpdateDescriptorSetWithTemplate(device, set, _activeTemplate.Template, _data.Pointer);
} }
public void CommitPushDescriptor(VulkanRenderer gd, CommandBufferScoped cbs, DescriptorSetTemplate template, PipelineLayout layout)
{
gd.PushDescriptorApi.CmdPushDescriptorSetWithTemplate(cbs.CommandBuffer, template.Template, layout, 0, _data.Pointer);
}
public void Dispose() public void Dispose()
{ {
_data?.Dispose(); _data?.Dispose();

View File

@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CompareOp = Ryujinx.Graphics.GAL.CompareOp; using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
using Format = Ryujinx.Graphics.GAL.Format; using Format = Ryujinx.Graphics.GAL.Format;
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
@ -34,6 +35,36 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
private record struct TextureRef
{
public ShaderStage Stage;
public TextureStorage Storage;
public Auto<DisposableImageView> View;
public Auto<DisposableSampler> Sampler;
public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
{
Stage = stage;
Storage = storage;
View = view;
Sampler = sampler;
}
}
private record struct ImageRef
{
public ShaderStage Stage;
public TextureStorage Storage;
public Auto<DisposableImageView> View;
public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
{
Stage = stage;
Storage = storage;
View = view;
}
}
private readonly VulkanRenderer _gd; private readonly VulkanRenderer _gd;
private readonly Device _device; private readonly Device _device;
private readonly PipelineBase _pipeline; private readonly PipelineBase _pipeline;
@ -41,9 +72,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly BufferRef[] _uniformBufferRefs; private readonly BufferRef[] _uniformBufferRefs;
private readonly BufferRef[] _storageBufferRefs; private readonly BufferRef[] _storageBufferRefs;
private readonly Auto<DisposableImageView>[] _textureRefs; private readonly TextureRef[] _textureRefs;
private readonly Auto<DisposableSampler>[] _samplerRefs; private readonly ImageRef[] _imageRefs;
private readonly Auto<DisposableImageView>[] _imageRefs;
private readonly TextureBuffer[] _bufferTextureRefs; private readonly TextureBuffer[] _bufferTextureRefs;
private readonly TextureBuffer[] _bufferImageRefs; private readonly TextureBuffer[] _bufferImageRefs;
private readonly Format[] _bufferImageFormats; private readonly Format[] _bufferImageFormats;
@ -61,6 +91,8 @@ namespace Ryujinx.Graphics.Vulkan
private BitMapStruct<Array2<long>> _storageSet; private BitMapStruct<Array2<long>> _storageSet;
private BitMapStruct<Array2<long>> _uniformMirrored; private BitMapStruct<Array2<long>> _uniformMirrored;
private BitMapStruct<Array2<long>> _storageMirrored; private BitMapStruct<Array2<long>> _storageMirrored;
private readonly int[] _uniformSetPd;
private int _pdSequence = 1;
private bool _updateDescriptorCacheCbIndex; private bool _updateDescriptorCacheCbIndex;
@ -92,9 +124,8 @@ namespace Ryujinx.Graphics.Vulkan
_uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; _uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
_storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; _storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
_textureRefs = new Auto<DisposableImageView>[Constants.MaxTextureBindings * 2]; _textureRefs = new TextureRef[Constants.MaxTextureBindings * 2];
_samplerRefs = new Auto<DisposableSampler>[Constants.MaxTextureBindings * 2]; _imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
_imageRefs = new Auto<DisposableImageView>[Constants.MaxImageBindings * 2];
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2]; _bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2]; _bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
_bufferImageFormats = new Format[Constants.MaxImageBindings * 2]; _bufferImageFormats = new Format[Constants.MaxImageBindings * 2];
@ -106,6 +137,8 @@ namespace Ryujinx.Graphics.Vulkan
_bufferTextures = new BufferView[Constants.MaxTexturesPerStage]; _bufferTextures = new BufferView[Constants.MaxTexturesPerStage];
_bufferImages = new BufferView[Constants.MaxImagesPerStage]; _bufferImages = new BufferView[Constants.MaxImagesPerStage];
_uniformSetPd = new int[Constants.MaxUniformBufferBindings];
var initialImageInfo = new DescriptorImageInfo var initialImageInfo = new DescriptorImageInfo
{ {
ImageLayout = ImageLayout.General, ImageLayout = ImageLayout.General,
@ -193,6 +226,7 @@ namespace Ryujinx.Graphics.Vulkan
if (BindingOverlaps(ref info, bindingOffset, offset, size)) if (BindingOverlaps(ref info, bindingOffset, offset, size))
{ {
_uniformSet.Clear(binding); _uniformSet.Clear(binding);
_uniformSetPd[binding] = 0;
SignalDirty(DirtyFlags.Uniform); SignalDirty(DirtyFlags.Uniform);
} }
} }
@ -223,14 +257,68 @@ namespace Ryujinx.Graphics.Vulkan
}); });
} }
public void SetProgram(ShaderCollection program) public void InsertBindingBarriers(CommandBufferScoped cbs)
{ {
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
{
if (segment.Type == ResourceType.TextureAndSampler)
{
for (int i = 0; i < segment.Count; i++)
{
ref var texture = ref _textureRefs[segment.Binding + i];
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
}
}
}
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.ImageSetIndex])
{
if (segment.Type == ResourceType.Image)
{
for (int i = 0; i < segment.Count; i++)
{
ref var image = ref _imageRefs[segment.Binding + i];
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
}
}
}
}
public void AdvancePdSequence()
{
if (++_pdSequence == 0)
{
_pdSequence = 1;
}
}
public void SetProgram(CommandBufferScoped cbs, ShaderCollection program, bool isBound)
{
if (!program.HasSameLayout(_program))
{
// When the pipeline layout changes, push descriptor bindings are invalidated.
AdvancePdSequence();
if (_gd.IsNvidiaPreTuring && !program.UsePushDescriptors && _program?.UsePushDescriptors == true && isBound)
{
// On older nvidia GPUs, we need to clear out the active push descriptor bindings when switching
// to normal descriptors. Keeping them bound can prevent buffers from binding properly in future.
ClearAndBindUniformBufferPd(cbs);
}
}
_program = program; _program = program;
_updateDescriptorCacheCbIndex = true; _updateDescriptorCacheCbIndex = true;
_dirty = DirtyFlags.All; _dirty = DirtyFlags.All;
} }
public void SetImage(int binding, ITexture image, Format imageFormat) public void SetImage(
CommandBufferScoped cbs,
ShaderStage stage,
int binding,
ITexture image,
Format imageFormat)
{ {
if (image is TextureBuffer imageBuffer) if (image is TextureBuffer imageBuffer)
{ {
@ -239,11 +327,13 @@ namespace Ryujinx.Graphics.Vulkan
} }
else if (image is TextureView view) else if (image is TextureView view)
{ {
_imageRefs[binding] = view.GetView(imageFormat).GetIdentityImageView(); view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
_imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
} }
else else
{ {
_imageRefs[binding] = null; _imageRefs[binding] = default;
_bufferImageRefs[binding] = null; _bufferImageRefs[binding] = null;
_bufferImageFormats[binding] = default; _bufferImageFormats[binding] = default;
} }
@ -253,7 +343,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetImage(int binding, Auto<DisposableImageView> image) public void SetImage(int binding, Auto<DisposableImageView> image)
{ {
_imageRefs[binding] = image; _imageRefs[binding] = new(ShaderStage.Compute, null, image);
SignalDirty(DirtyFlags.Image); SignalDirty(DirtyFlags.Image);
} }
@ -338,15 +428,13 @@ namespace Ryujinx.Graphics.Vulkan
} }
else if (texture is TextureView view) else if (texture is TextureView view)
{ {
view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
_textureRefs[binding] = view.GetImageView(); _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
} }
else else
{ {
_textureRefs[binding] = null; _textureRefs[binding] = default;
_samplerRefs[binding] = null;
_bufferTextureRefs[binding] = null; _bufferTextureRefs[binding] = null;
} }
@ -362,10 +450,9 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (texture is TextureView view) if (texture is TextureView view)
{ {
view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
_textureRefs[binding] = view.GetIdentityImageView(); _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
SignalDirty(DirtyFlags.Texture); SignalDirty(DirtyFlags.Texture);
} }
@ -402,6 +489,7 @@ namespace Ryujinx.Graphics.Vulkan
if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range) if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range)
{ {
_uniformSet.Clear(index); _uniformSet.Clear(index);
_uniformSetPd[index] = 0;
currentInfo = info; currentInfo = info;
currentBufferRef = newRef; currentBufferRef = newRef;
@ -579,9 +667,10 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
ref var texture = ref textures[i]; ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default; texture.ImageView = refs.View?.Get(cbs).Value ?? default;
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default; texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0) if (texture.ImageView.Handle == 0)
{ {
@ -616,7 +705,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default; images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
} }
tu.Push<DescriptorImageInfo>(images[..count]); tu.Push<DescriptorImageInfo>(images[..count]);
@ -671,15 +760,19 @@ namespace Ryujinx.Graphics.Vulkan
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp) private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
{ {
int sequence = _pdSequence;
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex]; var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
var dummyBuffer = _dummyBuffer?.GetBuffer(); var dummyBuffer = _dummyBuffer?.GetBuffer();
long updatedBindings = 0;
DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>());
foreach (ResourceBindingSegment segment in bindingSegments) foreach (ResourceBindingSegment segment in bindingSegments)
{ {
int binding = segment.Binding; int binding = segment.Binding;
int count = segment.Count; int count = segment.Count;
bool doUpdate = false; ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@ -688,17 +781,58 @@ namespace Ryujinx.Graphics.Vulkan
if (_uniformSet.Set(index)) if (_uniformSet.Set(index))
{ {
ref BufferRef buffer = ref _uniformBufferRefs[index]; ref BufferRef buffer = ref _uniformBufferRefs[index];
UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true);
doUpdate = true; bool mirrored = UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true);
_uniformMirrored.Set(index, mirrored);
}
if (_uniformSetPd[index] != sequence)
{
// Need to set this push descriptor (even if the buffer binding has not changed)
_uniformSetPd[index] = sequence;
updatedBindings |= 1L << index;
writer.Push(MemoryMarshal.CreateReadOnlySpan(ref _uniformBuffers[index], 1));
} }
} }
}
if (doUpdate) if (updatedBindings > 0)
{
DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
_templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout);
}
}
private void ClearAndBindUniformBufferPd(CommandBufferScoped cbs)
{
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
long updatedBindings = 0;
DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>());
foreach (ResourceBindingSegment segment in bindingSegments)
{
int binding = segment.Binding;
int count = segment.Count;
for (int i = 0; i < count; i++)
{ {
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; int index = binding + i;
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); updatedBindings |= 1L << index;
var bufferInfo = new DescriptorBufferInfo();
writer.Push(MemoryMarshal.CreateReadOnlySpan(ref bufferInfo, 1));
} }
} }
if (updatedBindings > 0)
{
DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
_templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout);
}
} }
private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc) private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc)
@ -724,6 +858,7 @@ namespace Ryujinx.Graphics.Vulkan
_uniformSet.Clear(); _uniformSet.Clear();
_storageSet.Clear(); _storageSet.Clear();
AdvancePdSequence();
} }
private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)

View File

@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
_pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();

View File

@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
_pipeline.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.SetImage(ShaderStage.Compute, 0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();

View File

@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer); buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
_pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();
@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
_pipeline.SetImage(0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();
@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.Specialize(_specConstants); _pipeline.Specialize(_specConstants);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
_pipeline.SetImage(0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)); _pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();

View File

@ -376,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
return format switch return format switch
{ {
Format.D16Unorm or Format.D32Float => ImageAspectFlags.DepthBit, Format.D16Unorm or Format.D32Float or Format.X8UintD24Unorm => ImageAspectFlags.DepthBit,
Format.S8Uint => ImageAspectFlags.StencilBit, Format.S8Uint => ImageAspectFlags.StencilBit,
Format.D24UnormS8Uint or Format.D24UnormS8Uint or
Format.D32FloatS8Uint or Format.D32FloatS8Uint or
@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
return format switch return format switch
{ {
Format.D16Unorm or Format.D32Float => ImageAspectFlags.DepthBit, Format.D16Unorm or Format.D32Float or Format.X8UintD24Unorm => ImageAspectFlags.DepthBit,
Format.S8Uint => ImageAspectFlags.StencilBit, Format.S8Uint => ImageAspectFlags.StencilBit,
Format.D24UnormS8Uint or Format.D24UnormS8Uint or
Format.D32FloatS8Uint or Format.D32FloatS8Uint or

View File

@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
public static bool IsD24S8(Format format) public static bool IsD24S8(Format format)
{ {
return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm; return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm || format == Format.X8UintD24Unorm;
} }
private static bool IsRGB16IntFloat(Format format) private static bool IsRGB16IntFloat(Format format)

View File

@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Vulkan
Add(Format.S8Uint, VkFormat.S8Uint); Add(Format.S8Uint, VkFormat.S8Uint);
Add(Format.D16Unorm, VkFormat.D16Unorm); Add(Format.D16Unorm, VkFormat.D16Unorm);
Add(Format.S8UintD24Unorm, VkFormat.D24UnormS8Uint); Add(Format.S8UintD24Unorm, VkFormat.D24UnormS8Uint);
Add(Format.X8UintD24Unorm, VkFormat.X8D24UnormPack32);
Add(Format.D32Float, VkFormat.D32Sfloat); Add(Format.D32Float, VkFormat.D32Sfloat);
Add(Format.D24UnormS8Uint, VkFormat.D24UnormS8Uint); Add(Format.D24UnormS8Uint, VkFormat.D24UnormS8Uint);
Add(Format.D32FloatS8Uint, VkFormat.D32SfloatS8Uint); Add(Format.D32FloatS8Uint, VkFormat.D32SfloatS8Uint);

View File

@ -243,41 +243,6 @@ namespace Ryujinx.Graphics.Vulkan
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments); return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
} }
public void UpdateModifications()
{
if (_colors != null)
{
for (int index = 0; index < _colors.Length; index++)
{
_colors[index].Storage.SetModification(
AccessFlags.ColorAttachmentWriteBit,
PipelineStageFlags.ColorAttachmentOutputBit);
}
}
_depthStencil?.Storage.SetModification(
AccessFlags.DepthStencilAttachmentWriteBit,
PipelineStageFlags.LateFragmentTestsBit);
}
public void InsertClearBarrier(CommandBufferScoped cbs, int index)
{
_colorsCanonical?[index]?.Storage?.InsertReadToWriteBarrier(
cbs,
AccessFlags.ColorAttachmentWriteBit,
PipelineStageFlags.ColorAttachmentOutputBit,
insideRenderPass: true);
}
public void InsertClearBarrierDS(CommandBufferScoped cbs)
{
_depthStencil?.Storage?.InsertReadToWriteBarrier(
cbs,
AccessFlags.DepthStencilAttachmentWriteBit,
PipelineStageFlags.LateFragmentTestsBit,
insideRenderPass: true);
}
public TextureView[] GetAttachmentViews() public TextureView[] GetAttachmentViews()
{ {
var result = new TextureView[_attachments.Length]; var result = new TextureView[_attachments.Length];
@ -297,23 +262,20 @@ namespace Ryujinx.Graphics.Vulkan
return new RenderPassCacheKey(_depthStencil, _colorsCanonical); return new RenderPassCacheKey(_depthStencil, _colorsCanonical);
} }
public void InsertLoadOpBarriers(CommandBufferScoped cbs) public void InsertLoadOpBarriers(VulkanRenderer gd, CommandBufferScoped cbs)
{ {
if (_colors != null) if (_colors != null)
{ {
foreach (var color in _colors) foreach (var color in _colors)
{ {
// If Clear or DontCare were used, this would need to be write bit. // If Clear or DontCare were used, this would need to be write bit.
color.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.ColorAttachmentReadBit, PipelineStageFlags.ColorAttachmentOutputBit); color.Storage?.QueueLoadOpBarrier(cbs, false);
color.Storage?.SetModification(AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit);
} }
} }
if (_depthStencil != null) _depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
{
_depthStencil.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.DepthStencilAttachmentReadBit, PipelineStageFlags.EarlyFragmentTestsBit); gd.Barriers.Flush(cbs.CommandBuffer, false, null);
_depthStencil.Storage?.SetModification(AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.LateFragmentTestsBit);
}
} }
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(

View File

@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsMultiView; public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors; public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPushDescriptors; public readonly bool SupportsPushDescriptors;
public readonly uint MaxPushDescriptors;
public readonly bool SupportsPrimitiveTopologyListRestart; public readonly bool SupportsPrimitiveTopologyListRestart;
public readonly bool SupportsPrimitiveTopologyPatchListRestart; public readonly bool SupportsPrimitiveTopologyPatchListRestart;
public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedback;
@ -71,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsMultiView, bool supportsMultiView,
bool supportsNullDescriptors, bool supportsNullDescriptors,
bool supportsPushDescriptors, bool supportsPushDescriptors,
uint maxPushDescriptors,
bool supportsPrimitiveTopologyListRestart, bool supportsPrimitiveTopologyListRestart,
bool supportsPrimitiveTopologyPatchListRestart, bool supportsPrimitiveTopologyPatchListRestart,
bool supportsTransformFeedback, bool supportsTransformFeedback,
@ -107,6 +109,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsMultiView = supportsMultiView; SupportsMultiView = supportsMultiView;
SupportsNullDescriptors = supportsNullDescriptors; SupportsNullDescriptors = supportsNullDescriptors;
SupportsPushDescriptors = supportsPushDescriptors; SupportsPushDescriptors = supportsPushDescriptors;
MaxPushDescriptors = maxPushDescriptors;
SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart; SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart;
SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart; SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart;
SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedback = supportsTransformFeedback;

View File

@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l); var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, dstFormat); _pipeline.SetImage(ShaderStage.Compute, 0, dstView, dstFormat);
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32; int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
@ -1168,7 +1168,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstView = Create2DLayerView(dst, dstLayer + z, 0); var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, format); _pipeline.SetImage(ShaderStage.Compute, 0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);

View File

@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
private PipelineState _newState; private PipelineState _newState;
private bool _graphicsStateDirty; private bool _graphicsStateDirty;
private bool _computeStateDirty; private bool _computeStateDirty;
private bool _bindingBarriersDirty;
private PrimitiveTopology _topology; private PrimitiveTopology _topology;
private ulong _currentPipelineHandle; private ulong _currentPipelineHandle;
@ -248,14 +249,14 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass(); CreateRenderPass();
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
BeginRenderPass(); BeginRenderPass();
var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha)); var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha));
var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue); var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue);
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
FramebufferParams.InsertClearBarrier(Cbs, index);
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
} }
@ -286,13 +287,13 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass(); CreateRenderPass();
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
BeginRenderPass(); BeginRenderPass();
var attachment = new ClearAttachment(flags, 0, clearValue); var attachment = new ClearAttachment(flags, 0, clearValue);
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
FramebufferParams.InsertClearBarrierDS(Cbs);
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
} }
@ -887,9 +888,9 @@ namespace Ryujinx.Graphics.Vulkan
SignalStateChange(); SignalStateChange();
} }
public void SetImage(int binding, ITexture image, Format imageFormat) public void SetImage(ShaderStage stage, int binding, ITexture image, Format imageFormat)
{ {
_descriptorSetUpdater.SetImage(binding, image, imageFormat); _descriptorSetUpdater.SetImage(Cbs, stage, binding, image, imageFormat);
} }
public void SetImage(int binding, Auto<DisposableImageView> image) public void SetImage(int binding, Auto<DisposableImageView> image)
@ -976,7 +977,8 @@ namespace Ryujinx.Graphics.Vulkan
_program = internalProgram; _program = internalProgram;
_descriptorSetUpdater.SetProgram(internalProgram); _descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0);
_bindingBarriersDirty = true;
_newState.PipelineLayout = internalProgram.PipelineLayout; _newState.PipelineLayout = internalProgram.PipelineLayout;
_newState.StagesCount = (uint)stages.Length; _newState.StagesCount = (uint)stages.Length;
@ -1066,7 +1068,6 @@ namespace Ryujinx.Graphics.Vulkan
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
{ {
CreateFramebuffer(colors, depthStencil, filterWriteMasked); CreateFramebuffer(colors, depthStencil, filterWriteMasked);
FramebufferParams?.UpdateModifications();
CreateRenderPass(); CreateRenderPass();
SignalStateChange(); SignalStateChange();
SignalAttachmentChange(); SignalAttachmentChange();
@ -1520,8 +1521,18 @@ namespace Ryujinx.Graphics.Vulkan
CreatePipeline(PipelineBindPoint.Compute); CreatePipeline(PipelineBindPoint.Compute);
_computeStateDirty = false; _computeStateDirty = false;
Pbp = PipelineBindPoint.Compute; Pbp = PipelineBindPoint.Compute;
if (_bindingBarriersDirty)
{
// Stale barriers may have been activated by switching program. Emit any that are relevant.
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
_bindingBarriersDirty = false;
}
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
} }
@ -1575,8 +1586,18 @@ namespace Ryujinx.Graphics.Vulkan
_graphicsStateDirty = false; _graphicsStateDirty = false;
Pbp = PipelineBindPoint.Graphics; Pbp = PipelineBindPoint.Graphics;
if (_bindingBarriersDirty)
{
// Stale barriers may have been activated by switching program. Emit any that are relevant.
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
_bindingBarriersDirty = false;
}
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
return true; return true;
@ -1630,6 +1651,8 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (!RenderPassActive) if (!RenderPassActive)
{ {
FramebufferParams.InsertLoadOpBarriers(Gd, Cbs);
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height)); var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
var clearValue = new ClearValue(); var clearValue = new ClearValue();

View File

@ -269,6 +269,7 @@ namespace Ryujinx.Graphics.Vulkan
PreloadCbs = null; PreloadCbs = null;
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, false, null);
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
Gd.RegisterFlush(); Gd.RegisterFlush();

View File

@ -31,6 +31,11 @@ namespace Ryujinx.Graphics.Vulkan
private int _dsLastCbIndex; private int _dsLastCbIndex;
private int _dsLastSubmissionCount; private int _dsLastSubmissionCount;
private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates;
private readonly ResourceDescriptorCollection _pdDescriptors;
private long _lastPdUsage;
private DescriptorSetTemplate _lastPdTemplate;
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount) private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
{ {
_gd = gd; _gd = gd;
@ -72,6 +77,12 @@ namespace Ryujinx.Graphics.Vulkan
_consumedDescriptorsPerSet[setIndex] = count; _consumedDescriptorsPerSet[setIndex] = count;
} }
if (usePushDescriptors)
{
_pdDescriptors = setDescriptors[0];
_pdTemplates = new();
}
} }
public void UpdateCommandBufferIndex(int commandBufferIndex) public void UpdateCommandBufferIndex(int commandBufferIndex)
@ -143,10 +154,39 @@ namespace Ryujinx.Graphics.Vulkan
return output[..count]; return output[..count];
} }
public DescriptorSetTemplate GetPushDescriptorTemplate(PipelineBindPoint pbp, long updateMask)
{
if (_lastPdUsage == updateMask && _lastPdTemplate != null)
{
// Most likely result is that it asks to update the same buffers.
return _lastPdTemplate;
}
if (!_pdTemplates.TryGetValue(updateMask, out DescriptorSetTemplate template))
{
template = new DescriptorSetTemplate(_gd, _device, _pdDescriptors, updateMask, this, pbp, 0);
_pdTemplates.Add(updateMask, template);
}
_lastPdUsage = updateMask;
_lastPdTemplate = template;
return template;
}
protected virtual unsafe void Dispose(bool disposing) protected virtual unsafe void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {
if (_pdTemplates != null)
{
foreach (DescriptorSetTemplate template in _pdTemplates.Values)
{
template.Dispose();
}
}
for (int i = 0; i < _dsCache.Length; i++) for (int i = 0; i < _dsCache.Length; i++)
{ {
for (int j = 0; j < _dsCache[i].Length; j++) for (int j = 0; j < _dsCache[i].Length; j++)

View File

@ -108,18 +108,25 @@ namespace Ryujinx.Graphics.Vulkan
_shaders = internalShaders; _shaders = internalShaders;
bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors; bool usePushDescriptors = !isMinimal &&
VulkanConfiguration.UsePushDescriptors &&
_gd.Capabilities.SupportsPushDescriptors &&
!IsCompute &&
CanUsePushDescriptors(gd, resourceLayout, IsCompute);
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors); ReadOnlyCollection<ResourceDescriptorCollection> sets = usePushDescriptors ?
BuildPushDescriptorSets(gd, resourceLayout.Sets) : resourceLayout.Sets;
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, sets, usePushDescriptors);
HasMinimalLayout = isMinimal; HasMinimalLayout = isMinimal;
UsePushDescriptors = usePushDescriptors; UsePushDescriptors = usePushDescriptors;
Stages = stages; Stages = stages;
ClearSegments = BuildClearSegments(resourceLayout.Sets); ClearSegments = BuildClearSegments(sets);
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages); BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
Templates = BuildTemplates(); Templates = BuildTemplates(usePushDescriptors);
_compileTask = Task.CompletedTask; _compileTask = Task.CompletedTask;
_firstBackgroundUse = false; _firstBackgroundUse = false;
@ -139,6 +146,76 @@ namespace Ryujinx.Graphics.Vulkan
_firstBackgroundUse = !fromCache; _firstBackgroundUse = !fromCache;
} }
private static bool CanUsePushDescriptors(VulkanRenderer gd, ResourceLayout layout, bool isCompute)
{
// If binding 3 is immediately used, use an alternate set of reserved bindings.
ReadOnlyCollection<ResourceUsage> uniformUsage = layout.SetUsages[0].Usages;
bool hasBinding3 = uniformUsage.Any(x => x.Binding == 3);
int[] reserved = isCompute ? Array.Empty<int>() : gd.GetPushDescriptorReservedBindings(hasBinding3);
// Can't use any of the reserved usages.
for (int i = 0; i < uniformUsage.Count; i++)
{
var binding = uniformUsage[i].Binding;
if (reserved.Contains(binding) ||
binding >= Constants.MaxPushDescriptorBinding ||
binding >= gd.Capabilities.MaxPushDescriptors + reserved.Count(id => id < binding))
{
return false;
}
}
return true;
}
private static ReadOnlyCollection<ResourceDescriptorCollection> BuildPushDescriptorSets(
VulkanRenderer gd,
ReadOnlyCollection<ResourceDescriptorCollection> sets)
{
// The reserved bindings were selected when determining if push descriptors could be used.
int[] reserved = gd.GetPushDescriptorReservedBindings(false);
var result = new ResourceDescriptorCollection[sets.Count];
for (int i = 0; i < sets.Count; i++)
{
if (i == 0)
{
// Push descriptors apply here. Remove reserved bindings.
ResourceDescriptorCollection original = sets[i];
var pdUniforms = new ResourceDescriptor[original.Descriptors.Count];
int j = 0;
foreach (ResourceDescriptor descriptor in original.Descriptors)
{
if (reserved.Contains(descriptor.Binding))
{
// If the binding is reserved, set its descriptor count to 0.
pdUniforms[j++] = new ResourceDescriptor(
descriptor.Binding,
0,
descriptor.Type,
descriptor.Stages);
}
else
{
pdUniforms[j++] = descriptor;
}
}
result[i] = new ResourceDescriptorCollection(new(pdUniforms));
}
else
{
result[i] = sets[i];
}
}
return new(result);
}
private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets) private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets)
{ {
ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][]; ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][];
@ -243,12 +320,18 @@ namespace Ryujinx.Graphics.Vulkan
return segments; return segments;
} }
private DescriptorSetTemplate[] BuildTemplates() private DescriptorSetTemplate[] BuildTemplates(bool usePushDescriptors)
{ {
var templates = new DescriptorSetTemplate[BindingSegments.Length]; var templates = new DescriptorSetTemplate[BindingSegments.Length];
for (int setIndex = 0; setIndex < BindingSegments.Length; setIndex++) for (int setIndex = 0; setIndex < BindingSegments.Length; setIndex++)
{ {
if (usePushDescriptors && setIndex == 0)
{
// Push descriptors get updated using templates owned by the pipeline layout.
continue;
}
ResourceBindingSegment[] segments = BindingSegments[setIndex]; ResourceBindingSegment[] segments = BindingSegments[setIndex];
if (segments != null && segments.Length > 0) if (segments != null && segments.Length > 0)
@ -433,6 +516,11 @@ namespace Ryujinx.Graphics.Vulkan
return null; return null;
} }
public DescriptorSetTemplate GetPushDescriptorTemplate(long updateMask)
{
return _plce.GetPushDescriptorTemplate(IsCompute ? PipelineBindPoint.Compute : PipelineBindPoint.Graphics, updateMask);
}
public void AddComputePipeline(ref SpecData key, Auto<DisposablePipeline> pipeline) public void AddComputePipeline(ref SpecData key, Auto<DisposablePipeline> pipeline)
{ {
(_computePipelineCache ??= new()).Add(ref key, pipeline); (_computePipelineCache ??= new()).Add(ref key, pipeline);
@ -493,6 +581,11 @@ namespace Ryujinx.Graphics.Vulkan
return _plce.GetNewDescriptorSetCollection(setIndex, out isNew); return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
} }
public bool HasSameLayout(ShaderCollection other)
{
return other != null && _plce == other._plce;
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)

View File

@ -154,9 +154,8 @@ namespace Ryujinx.Graphics.Vulkan
{ {
Format.S8Uint => Format.R8Unorm, Format.S8Uint => Format.R8Unorm,
Format.D16Unorm => Format.R16Unorm, Format.D16Unorm => Format.R16Unorm,
Format.S8UintD24Unorm => Format.R8G8B8A8Unorm, Format.D24UnormS8Uint or Format.S8UintD24Unorm or Format.X8UintD24Unorm => Format.R8G8B8A8Unorm,
Format.D32Float => Format.R32Float, Format.D32Float => Format.R32Float,
Format.D24UnormS8Uint => Format.R8G8B8A8Unorm,
Format.D32FloatS8Uint => Format.R32G32Float, Format.D32FloatS8Uint => Format.R32G32Float,
_ => throw new ArgumentException($"\"{format}\" is not a supported depth or stencil format."), _ => throw new ArgumentException($"\"{format}\" is not a supported depth or stencil format."),
}; };
@ -434,99 +433,65 @@ namespace Ryujinx.Graphics.Vulkan
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint; return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
} }
public void SetModification(AccessFlags accessFlags, PipelineStageFlags stage) public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{ {
_lastModificationAccess = accessFlags; PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
_lastModificationStage = stage; PipelineStageFlags dstStageFlags = depthStencil ?
} PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit :
PipelineStageFlags.ColorAttachmentOutputBit;
public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags, bool insideRenderPass) AccessFlags srcAccessFlags = _lastModificationAccess | _lastReadAccess;
{ AccessFlags dstAccessFlags = depthStencil ?
var lastReadStage = _lastReadStage; AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.DepthStencilAttachmentReadBit :
AccessFlags.ColorAttachmentWriteBit | AccessFlags.ColorAttachmentReadBit;
if (insideRenderPass) if (srcAccessFlags != AccessFlags.None)
{ {
// We can't have barrier from compute inside a render pass, ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
// as it is invalid to specify compute in the subpass dependency stage mask. ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
_imageAuto.Get(cbs).Value,
srcAccessFlags,
dstAccessFlags,
aspectFlags,
0,
0,
_info.GetLayers(),
_info.Levels);
lastReadStage &= ~PipelineStageFlags.ComputeShaderBit; _gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags);
}
if (lastReadStage != PipelineStageFlags.None)
{
// This would result in a validation error, but is
// required on MoltenVK as the generic barrier results in
// severe texture flickering in some scenarios.
if (_gd.IsMoltenVk)
{
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
TextureView.InsertImageBarrier(
_gd.Api,
cbs.CommandBuffer,
_imageAuto.Get(cbs).Value,
_lastReadAccess,
dstAccessFlags,
_lastReadStage,
dstStageFlags,
aspectFlags,
0,
0,
_info.GetLayers(),
_info.Levels);
}
else
{
TextureView.InsertMemoryBarrier(
_gd.Api,
cbs.CommandBuffer,
_lastReadAccess,
dstAccessFlags,
lastReadStage,
dstStageFlags);
}
_lastReadAccess = AccessFlags.None;
_lastReadStage = PipelineStageFlags.None; _lastReadStage = PipelineStageFlags.None;
_lastReadAccess = AccessFlags.None;
} }
_lastModificationStage = depthStencil ?
PipelineStageFlags.LateFragmentTestsBit :
PipelineStageFlags.ColorAttachmentOutputBit;
_lastModificationAccess = depthStencil ?
AccessFlags.DepthStencilAttachmentWriteBit :
AccessFlags.ColorAttachmentWriteBit;
} }
public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) public void QueueWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
{ {
_lastReadAccess |= dstAccessFlags; _lastReadAccess |= dstAccessFlags;
_lastReadStage |= dstStageFlags; _lastReadStage |= dstStageFlags;
if (_lastModificationAccess != AccessFlags.None) if (_lastModificationAccess != AccessFlags.None)
{ {
// This would result in a validation error, but is ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
// required on MoltenVK as the generic barrier results in ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
// severe texture flickering in some scenarios. _imageAuto.Get(cbs).Value,
if (_gd.IsMoltenVk) _lastModificationAccess,
{ dstAccessFlags,
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); aspectFlags,
TextureView.InsertImageBarrier( 0,
_gd.Api, 0,
cbs.CommandBuffer, _info.GetLayers(),
_imageAuto.Get(cbs).Value, _info.Levels);
_lastModificationAccess,
dstAccessFlags, _gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
_lastModificationStage,
dstStageFlags,
aspectFlags,
0,
0,
_info.GetLayers(),
_info.Levels);
}
else
{
TextureView.InsertMemoryBarrier(
_gd.Api,
cbs.CommandBuffer,
_lastModificationAccess,
dstAccessFlags,
_lastModificationStage,
dstStageFlags);
}
_lastModificationAccess = AccessFlags.None; _lastModificationAccess = AccessFlags.None;
} }

View File

@ -497,6 +497,30 @@ namespace Ryujinx.Graphics.Vulkan
null); null);
} }
public static ImageMemoryBarrier GetImageBarrier(
Image image,
AccessFlags srcAccessMask,
AccessFlags dstAccessMask,
ImageAspectFlags aspectFlags,
int firstLayer,
int firstLevel,
int layers,
int levels)
{
return new()
{
SType = StructureType.ImageMemoryBarrier,
SrcAccessMask = srcAccessMask,
DstAccessMask = dstAccessMask,
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
Image = image,
OldLayout = ImageLayout.General,
NewLayout = ImageLayout.General,
SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers),
};
}
public static unsafe void InsertImageBarrier( public static unsafe void InsertImageBarrier(
Vk api, Vk api,
CommandBuffer commandBuffer, CommandBuffer commandBuffer,
@ -511,18 +535,15 @@ namespace Ryujinx.Graphics.Vulkan
int layers, int layers,
int levels) int levels)
{ {
ImageMemoryBarrier memoryBarrier = new() ImageMemoryBarrier memoryBarrier = GetImageBarrier(
{ image,
SType = StructureType.ImageMemoryBarrier, srcAccessMask,
SrcAccessMask = srcAccessMask, dstAccessMask,
DstAccessMask = dstAccessMask, aspectFlags,
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, firstLayer,
DstQueueFamilyIndex = Vk.QueueFamilyIgnored, firstLevel,
Image = image, layers,
OldLayout = ImageLayout.General, levels);
NewLayout = ImageLayout.General,
SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers),
};
api.CmdPipelineBarrier( api.CmdPipelineBarrier(
commandBuffer, commandBuffer,

View File

@ -20,6 +20,9 @@ namespace Ryujinx.Graphics.Vulkan
[GeneratedRegex("Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")] [GeneratedRegex("Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
public static partial Regex AmdGcnRegex(); public static partial Regex AmdGcnRegex();
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
public static partial Regex NvidiaConsumerClassRegex();
public static Vendor FromId(uint id) public static Vendor FromId(uint id)
{ {
return id switch return id switch

View File

@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
public const bool UseFastBufferUpdates = true; public const bool UseFastBufferUpdates = true;
public const bool UseUnsafeBlit = true; public const bool UseUnsafeBlit = true;
public const bool UsePushDescriptors = false; public const bool UsePushDescriptors = true;
public const bool ForceD24S8Unsupported = false; public const bool ForceD24S8Unsupported = false;
public const bool ForceRGB16IntFloatUnsupported = false; public const bool ForceRGB16IntFloatUnsupported = false;

View File

@ -486,20 +486,6 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresFragmentShaderInterlock; pExtendedFeatures = &featuresFragmentShaderInterlock;
} }
PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"))
{
featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT
{
SType = StructureType.PhysicalDeviceSubgroupSizeControlFeaturesExt,
PNext = pExtendedFeatures,
SubgroupSizeControl = true,
};
pExtendedFeatures = &featuresSubgroupSizeControl;
}
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") && if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&

View File

@ -68,6 +68,8 @@ namespace Ryujinx.Graphics.Vulkan
internal HelperShader HelperShader { get; private set; } internal HelperShader HelperShader { get; private set; }
internal PipelineFull PipelineInternal => _pipeline; internal PipelineFull PipelineInternal => _pipeline;
internal BarrierBatch Barriers { get; private set; }
public IPipeline Pipeline => _pipeline; public IPipeline Pipeline => _pipeline;
public IWindow Window => _window; public IWindow Window => _window;
@ -76,10 +78,15 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Func<string[]> _getRequiredExtensions; private readonly Func<string[]> _getRequiredExtensions;
private readonly string _preferredGpuId; private readonly string _preferredGpuId;
private int[] _pdReservedBindings;
private readonly static int[] _pdReservedBindingsNvn = { 3, 18, 21, 36, 30 };
private readonly static int[] _pdReservedBindingsOgl = { 17, 18, 34, 35, 36 };
internal Vendor Vendor { get; private set; } internal Vendor Vendor { get; private set; }
internal bool IsAmdWindows { get; private set; } internal bool IsAmdWindows { get; private set; }
internal bool IsIntelWindows { get; private set; } internal bool IsIntelWindows { get; private set; }
internal bool IsAmdGcn { get; private set; } internal bool IsAmdGcn { get; private set; }
internal bool IsNvidiaPreTuring { get; private set; }
internal bool IsMoltenVk { get; private set; } internal bool IsMoltenVk { get; private set; }
internal bool IsTBDR { get; private set; } internal bool IsTBDR { get; private set; }
internal bool IsSharedMemory { get; private set; } internal bool IsSharedMemory { get; private set; }
@ -191,6 +198,19 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr, SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr,
}; };
bool supportsPushDescriptors = _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName);
PhysicalDevicePushDescriptorPropertiesKHR propertiesPushDescriptor = new PhysicalDevicePushDescriptorPropertiesKHR()
{
SType = StructureType.PhysicalDevicePushDescriptorPropertiesKhr
};
if (supportsPushDescriptors)
{
propertiesPushDescriptor.PNext = properties2.PNext;
properties2.PNext = &propertiesPushDescriptor;
}
PhysicalDeviceFeatures2 features2 = new() PhysicalDeviceFeatures2 features2 = new()
{ {
SType = StructureType.PhysicalDeviceFeatures2, SType = StructureType.PhysicalDeviceFeatures2,
@ -320,7 +340,8 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
featuresRobustness2.NullDescriptor || IsMoltenVk, featuresRobustness2.NullDescriptor || IsMoltenVk,
_physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName), supportsPushDescriptors && !IsMoltenVk,
propertiesPushDescriptor.MaxPushDescriptors,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
supportsTransformFeedback, supportsTransformFeedback,
@ -362,6 +383,8 @@ namespace Ryujinx.Graphics.Vulkan
HelperShader = new HelperShader(this, _device); HelperShader = new HelperShader(this, _device);
Barriers = new BarrierBatch(this);
_counters = new Counters(this, _device, _pipeline); _counters = new Counters(this, _device, _pipeline);
} }
@ -400,6 +423,25 @@ namespace Ryujinx.Graphics.Vulkan
_initialized = true; _initialized = true;
} }
internal int[] GetPushDescriptorReservedBindings(bool isOgl)
{
// The first call of this method determines what push descriptor layout is used for all shaders on this renderer.
// This is chosen to minimize shaders that can't fit their uniforms on the device's max number of push descriptors.
if (_pdReservedBindings == null)
{
if (Capabilities.MaxPushDescriptors <= Constants.MaxUniformBuffersPerStage * 2)
{
_pdReservedBindings = isOgl ? _pdReservedBindingsOgl : _pdReservedBindingsNvn;
}
else
{
_pdReservedBindings = Array.Empty<int>();
}
}
return _pdReservedBindings;
}
public BufferHandle CreateBuffer(int size, BufferAccess access) public BufferHandle CreateBuffer(int size, BufferAccess access)
{ {
return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream); return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream);
@ -716,6 +758,20 @@ namespace Ryujinx.Graphics.Vulkan
IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer); IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
if (Vendor == Vendor.Nvidia)
{
var match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer);
if (match != null && int.TryParse(match.Groups[2].Value, out int gpuNumber))
{
IsNvidiaPreTuring = gpuNumber < 2000;
}
else if (GpuDriver.Contains("TITAN") && !GpuDriver.Contains("RTX"))
{
IsNvidiaPreTuring = true;
}
}
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
} }
@ -862,6 +918,7 @@ namespace Ryujinx.Graphics.Vulkan
BufferManager.Dispose(); BufferManager.Dispose();
DescriptorSetManager.Dispose(); DescriptorSetManager.Dispose();
PipelineLayoutCache.Dispose(); PipelineLayoutCache.Dispose();
Barriers.Dispose();
MemoryAllocator.Dispose(); MemoryAllocator.Dispose();

View File

@ -673,9 +673,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryState.UnmapProcessCodeMemoryAllowed, MemoryState.UnmapProcessCodeMemoryAllowed,
KMemoryPermission.None, KMemoryPermission.None,
KMemoryPermission.None, KMemoryPermission.None,
MemoryAttribute.Mask, MemoryAttribute.Mask & ~MemoryAttribute.PermissionLocked,
MemoryAttribute.None, MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped | MemoryAttribute.PermissionLocked, MemoryAttribute.IpcAndDeviceMapped,
out MemoryState state, out MemoryState state,
out _, out _,
out _); out _);

View File

@ -1821,5 +1821,18 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(1004)] // 17.0.0+
// SetTouchScreenResolution(int width, int height, nn::applet::AppletResourceUserId)
public ResultCode SetTouchScreenResolution(ServiceCtx context)
{
int width = context.RequestData.ReadInt32();
int height = context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { width, height, appletResourceUserId });
return ResultCode.Success;
}
} }
} }

View File

@ -50,6 +50,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
case 0x06: case 0x06:
result = CallIoctlMethod<GetTpcMasksArguments>(GetTpcMasks, arguments); result = CallIoctlMethod<GetTpcMasksArguments>(GetTpcMasks, arguments);
break; break;
case 0x12:
result = CallIoctlMethod<NumVsmsArguments>(NumVsms, arguments);
break;
case 0x13:
result = CallIoctlMethod<VsmsMappingArguments>(VsmsMapping, arguments);
break;
case 0x14: case 0x14:
result = CallIoctlMethod<GetActiveSlotMaskArguments>(GetActiveSlotMask, arguments); result = CallIoctlMethod<GetActiveSlotMaskArguments>(GetActiveSlotMask, arguments);
break; break;
@ -76,6 +82,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
case 0x06: case 0x06:
result = CallIoctlMethod<GetTpcMasksArguments, int>(GetTpcMasks, arguments, inlineOutBuffer); result = CallIoctlMethod<GetTpcMasksArguments, int>(GetTpcMasks, arguments, inlineOutBuffer);
break; break;
case 0x12:
result = CallIoctlMethod<NumVsmsArguments>(NumVsms, arguments);
break;
case 0x13:
result = CallIoctlMethod<VsmsMappingArguments>(VsmsMapping, arguments);
break;
} }
} }
@ -216,6 +228,27 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
return NvInternalResult.Success; return NvInternalResult.Success;
} }
private NvInternalResult NumVsms(ref NumVsmsArguments arguments)
{
Logger.Stub?.PrintStub(LogClass.ServiceNv);
arguments.NumVsms = 2;
return NvInternalResult.Success;
}
private NvInternalResult VsmsMapping(ref VsmsMappingArguments arguments)
{
Logger.Stub?.PrintStub(LogClass.ServiceNv);
arguments.Sm0GpcIndex = 0;
arguments.Sm0TpcIndex = 0;
arguments.Sm1GpcIndex = 0;
arguments.Sm1TpcIndex = 1;
return NvInternalResult.Success;
}
private NvInternalResult GetActiveSlotMask(ref GetActiveSlotMaskArguments arguments) private NvInternalResult GetActiveSlotMask(ref GetActiveSlotMaskArguments arguments)
{ {
Logger.Stub?.PrintStub(LogClass.ServiceNv); Logger.Stub?.PrintStub(LogClass.ServiceNv);

View File

@ -0,0 +1,11 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
{
[StructLayout(LayoutKind.Sequential)]
struct NumVsmsArguments
{
public uint NumVsms;
public uint Reserved;
}
}

View File

@ -0,0 +1,13 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
{
[StructLayout(LayoutKind.Sequential)]
struct VsmsMappingArguments
{
public byte Sm0GpcIndex;
public byte Sm0TpcIndex;
public byte Sm1GpcIndex;
public byte Sm1TpcIndex;
}
}

View File

@ -78,7 +78,8 @@ namespace Ryujinx.Modules
} }
else if (OperatingSystem.IsLinux()) else if (OperatingSystem.IsLinux())
{ {
_platformExt = "linux_x64.tar.gz"; var arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64";
_platformExt = $"linux_{arch}.tar.gz";
artifactIndex = 0; artifactIndex = 0;
} }
@ -512,16 +513,6 @@ namespace Ryujinx.Modules
public static bool CanUpdate(bool showWarnings) public static bool CanUpdate(bool showWarnings)
{ {
#if !DISABLE_UPDATER #if !DISABLE_UPDATER
if (RuntimeInformation.OSArchitecture != Architecture.X64)
{
if (showWarnings)
{
GtkDialog.CreateWarningDialog("You are not running a supported system architecture!", "(Only x64 systems are supported!)");
}
return false;
}
if (!NetworkInterface.GetIsNetworkAvailable()) if (!NetworkInterface.GetIsNetworkAvailable())
{ {
if (showWarnings) if (showWarnings)