Compare commits

...

9 Commits

Author SHA1 Message Date
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
46 changed files with 967 additions and 280 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

@ -232,10 +232,17 @@ namespace Ryujinx.Common.Configuration
// Check if existing old baseDirPath is a symlink, to prevent possible errors. // Check if existing old baseDirPath is a symlink, to prevent possible errors.
// 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)
{
try
{ {
FileAttributes attributes = File.GetAttributes(path); FileAttributes attributes = File.GetAttributes(path);
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint; return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
} }
catch
{
return false;
}
}
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
public static void FixMacOSConfigurationFolders() public static void FixMacOSConfigurationFolders()

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,13 +29,13 @@ 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)
{ {
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); _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++)
{
int index = binding + i;
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,7 +1521,17 @@ 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,7 +1586,17 @@ 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);
@ -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,
// as it is invalid to specify compute in the subpass dependency stage mask.
lastReadStage &= ~PipelineStageFlags.ComputeShaderBit;
}
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(); ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
TextureView.InsertImageBarrier( ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
_gd.Api,
cbs.CommandBuffer,
_imageAuto.Get(cbs).Value, _imageAuto.Get(cbs).Value,
_lastReadAccess, srcAccessFlags,
dstAccessFlags, dstAccessFlags,
_lastReadStage,
dstStageFlags,
aspectFlags, aspectFlags,
0, 0,
0, 0,
_info.GetLayers(), _info.GetLayers(),
_info.Levels); _info.Levels);
}
else
{
TextureView.InsertMemoryBarrier(
_gd.Api,
cbs.CommandBuffer,
_lastReadAccess,
dstAccessFlags,
lastReadStage,
dstStageFlags);
}
_lastReadAccess = AccessFlags.None; _gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags);
_lastReadStage = PipelineStageFlags.None; _lastReadStage = PipelineStageFlags.None;
} _lastReadAccess = AccessFlags.None;
} }
public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) _lastModificationStage = depthStencil ?
PipelineStageFlags.LateFragmentTestsBit :
PipelineStageFlags.ColorAttachmentOutputBit;
_lastModificationAccess = depthStencil ?
AccessFlags.DepthStencilAttachmentWriteBit :
AccessFlags.ColorAttachmentWriteBit;
}
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
// required on MoltenVK as the generic barrier results in
// severe texture flickering in some scenarios.
if (_gd.IsMoltenVk)
{ {
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
TextureView.InsertImageBarrier( ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
_gd.Api,
cbs.CommandBuffer,
_imageAuto.Get(cbs).Value, _imageAuto.Get(cbs).Value,
_lastModificationAccess, _lastModificationAccess,
dstAccessFlags, dstAccessFlags,
_lastModificationStage,
dstStageFlags,
aspectFlags, aspectFlags,
0, 0,
0, 0,
_info.GetLayers(), _info.GetLayers(),
_info.Levels); _info.Levels);
}
else _gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
{
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)