Compare commits

...

8 Commits

Author SHA1 Message Date
riperiperi
ba91f5d401 Vulkan: Properly reset barrier batch when splitting due to mismatching flags (#6345)
Forgot to set the end variable here. Should stop it from crashing when this path is taken.
2024-02-22 10:43:22 +01:00
riperiperi
79f6c18a9b Vulkan: Disable push descriptors on older NVIDIA GPUs (#6340)
Disables push descriptors on older NVIDIA GPUs (10xx and below), since it is clearly broken beyond comprehension. The existing workaround wasn't good enough and a more thorough one will probably cost more performance than the feature gains. The workaround has been removed.

Fixes #6331.
2024-02-21 23:52:13 -03:00
riperiperi
4f63782bac Vulkan: Fix barrier batching past limit (#6339)
If more than 16 barriers were queued at one time, the _queuedBarrierCount would no longer match the number of remaining barriers, because when breaking out of the loop consuming them it deleted all barriers, not just the 16 that were consumed.

Should fix freezes that started occurring with #6240. Fixes issue #6338.
2024-02-21 23:41:08 -03:00
MetrosexualGarbodor
6f5fcb7970 Avalonia UI: Update English tooltips (#6305)
* update English tooltips

* missed a dot
2024-02-19 23:27:15 +01:00
Mary Guillemard
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
gdkchan
42340fc743 LightningJit: Add a limit on the number of instructions per function for Arm64 (#6328) 2024-02-17 17:30:54 -03:00
Exhigh
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
riperiperi
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
23 changed files with 480 additions and 229 deletions

View File

@@ -155,7 +155,7 @@
"SettingsTabGraphicsResolutionScaleNative": "Native (720p/1080p)",
"SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)",
"SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)",
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p)",
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)",
"SettingsTabGraphicsAspectRatio": "Aspect Ratio:",
"SettingsTabGraphicsAspectRatio4x3": "4:3",
"SettingsTabGraphicsAspectRatio16x9": "16:9",
@@ -452,13 +452,13 @@
"CustomThemePathTooltip": "Path to custom GUI theme",
"CustomThemeBrowseTooltip": "Browse for a custom GUI theme",
"DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.",
"DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.",
"DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.",
"DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.",
"DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.",
"RegionTooltip": "Change System Region",
"LanguageTooltip": "Change System Language",
"TimezoneTooltip": "Change System TimeZone",
"TimeTooltip": "Change System Time",
"VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference. We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.",
"VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.",
"PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.",
"FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.",
"AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.",
@@ -472,10 +472,10 @@
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
"ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.",
"ResolutionScaleTooltip": "Resolution Scale applied to applicable render targets",
"ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.",
"ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.",
"AnisotropyTooltip": "Level of Anisotropic Filtering (set to Auto to use the value requested by the game)",
"AspectRatioTooltip": "Aspect Ratio applied to the renderer window.",
"AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.",
"AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.",
"ShaderDumpPathTooltip": "Graphics Shaders Dump Path",
"FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.",
"StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.",
@@ -616,9 +616,9 @@
"UserProfilesName": "Name:",
"UserProfilesUserId": "User ID:",
"SettingsTabGraphicsBackend": "Graphics Backend",
"SettingsTabGraphicsBackendTooltip": "Graphics Backend to use",
"SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.",
"SettingsEnableTextureRecompression": "Enable Texture Recompression",
"SettingsEnableTextureRecompressionTooltip": "Compresses certain textures in order to reduce VRAM usage.\n\nRecommended for use with GPUs that have less than 4GiB VRAM.\n\nLeave OFF if unsure.",
"SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.",
"SettingsTabGraphicsPreferredGpu": "Preferred GPU",
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
@@ -644,12 +644,12 @@
"Recover": "Recover",
"UserProfilesRecoverHeading": "Saves were found for the following accounts",
"UserProfilesRecoverEmptyList": "No profiles to recover",
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
"GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
"GraphicsAALabel": "Anti-Aliasing:",
"GraphicsScalingFilterLabel": "Scaling Filter:",
"GraphicsScalingFilterTooltip": "Enables Framebuffer Scaling",
"GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
"GraphicsScalingFilterLevelLabel": "Level",
"GraphicsScalingFilterLevelTooltip": "Set Scaling Filter Level",
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
"SmaaLow": "SMAA Low",
"SmaaMedium": "SMAA Medium",
"SmaaHigh": "SMAA High",
@@ -657,12 +657,12 @@
"UserEditorTitle": "Edit User",
"UserEditorTitleCreate": "Create User",
"SettingsTabNetworkInterface": "Network Interface:",
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features",
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.",
"NetworkInterfaceDefault": "Default",
"PackagingShaders": "Packaging Shaders",
"AboutChangelogButton": "View Changelog on GitHub",
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.",
"SettingsTabNetworkMultiplayer": "Multiplayer",
"MultiplayerMode": "Mode:",
"MultiplayerModeTooltip": "Change multiplayer mode"
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure."
}

View File

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

View File

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

View File

@@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL
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);

View File

@@ -1,17 +1,20 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand>
{
public readonly CommandType CommandType => CommandType.SetImage;
private ShaderStage _stage;
private int _binding;
private TableRef<ITexture> _texture;
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;
_texture = texture;
_imageFormat = imageFormat;
@@ -19,7 +22,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
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();
}
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();
}

View File

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

View File

@@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
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
{

View File

@@ -935,7 +935,7 @@ namespace Ryujinx.Graphics.OpenGL
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)
{

View File

@@ -0,0 +1,229 @@
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;
int end = list.Count;
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)
{
end = i + 1;
break;
}
}
else
{
if (flags.Equals(barrier.Flags))
{
target[count++] = barrier.Barrier;
queuedBarrierCount--;
if (firstMatch == -1)
{
firstMatch = i;
}
if (count >= target.Length)
{
end = i + 1;
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;
end = list.Count;
}
}
}
}
if (firstMatch == 0 && end == list.Count)
{
list.Clear();
}
else if (firstMatch != -1)
{
int deleteCount = end - 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

@@ -35,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 Device _device;
private readonly PipelineBase _pipeline;
@@ -42,9 +72,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly BufferRef[] _uniformBufferRefs;
private readonly BufferRef[] _storageBufferRefs;
private readonly Auto<DisposableImageView>[] _textureRefs;
private readonly Auto<DisposableSampler>[] _samplerRefs;
private readonly Auto<DisposableImageView>[] _imageRefs;
private readonly TextureRef[] _textureRefs;
private readonly ImageRef[] _imageRefs;
private readonly TextureBuffer[] _bufferTextureRefs;
private readonly TextureBuffer[] _bufferImageRefs;
private readonly Format[] _bufferImageFormats;
@@ -95,9 +124,8 @@ namespace Ryujinx.Graphics.Vulkan
_uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
_storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
_textureRefs = new Auto<DisposableImageView>[Constants.MaxTextureBindings * 2];
_samplerRefs = new Auto<DisposableSampler>[Constants.MaxTextureBindings * 2];
_imageRefs = new Auto<DisposableImageView>[Constants.MaxImageBindings * 2];
_textureRefs = new TextureRef[Constants.MaxTextureBindings * 2];
_imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
_bufferImageFormats = new Format[Constants.MaxImageBindings * 2];
@@ -229,6 +257,33 @@ namespace Ryujinx.Graphics.Vulkan
});
}
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)
@@ -244,13 +299,6 @@ namespace Ryujinx.Graphics.Vulkan
// 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;
@@ -258,7 +306,12 @@ namespace Ryujinx.Graphics.Vulkan
_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)
{
@@ -267,11 +320,13 @@ namespace Ryujinx.Graphics.Vulkan
}
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
{
_imageRefs[binding] = null;
_imageRefs[binding] = default;
_bufferImageRefs[binding] = null;
_bufferImageFormats[binding] = default;
}
@@ -281,7 +336,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetImage(int binding, Auto<DisposableImageView> image)
{
_imageRefs[binding] = image;
_imageRefs[binding] = new(ShaderStage.Compute, null, image);
SignalDirty(DirtyFlags.Image);
}
@@ -366,15 +421,13 @@ namespace Ryujinx.Graphics.Vulkan
}
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();
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
_textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
}
else
{
_textureRefs[binding] = null;
_samplerRefs[binding] = null;
_textureRefs[binding] = default;
_bufferTextureRefs[binding] = null;
}
@@ -390,10 +443,9 @@ namespace Ryujinx.Graphics.Vulkan
{
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();
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
_textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
SignalDirty(DirtyFlags.Texture);
}
@@ -608,9 +660,10 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++)
{
ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
{
@@ -645,7 +698,7 @@ namespace Ryujinx.Graphics.Vulkan
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]);
@@ -746,35 +799,6 @@ namespace Ryujinx.Graphics.Vulkan
}
}
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)
{
// We don't support clearing texture descriptors currently.

View File

@@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
_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.ComputeBarrier();

View File

@@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var dispatchX = BitUtils.DivRoundUp(view.Width, 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.ComputeBarrier();

View File

@@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
_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.ComputeBarrier();
@@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _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.ComputeBarrier();
@@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.Specialize(_specConstants);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _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.ComputeBarrier();

View File

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

View File

@@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
_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 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);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, format);
_pipeline.SetImage(ShaderStage.Compute, 0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);

View File

@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
private PipelineState _newState;
private bool _graphicsStateDirty;
private bool _computeStateDirty;
private bool _bindingBarriersDirty;
private PrimitiveTopology _topology;
private ulong _currentPipelineHandle;
@@ -248,14 +249,14 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
BeginRenderPass();
var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha));
var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue);
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
FramebufferParams.InsertClearBarrier(Cbs, index);
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
}
@@ -286,13 +287,13 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
BeginRenderPass();
var attachment = new ClearAttachment(flags, 0, clearValue);
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
FramebufferParams.InsertClearBarrierDS(Cbs);
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
}
@@ -887,9 +888,9 @@ namespace Ryujinx.Graphics.Vulkan
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)
@@ -977,6 +978,7 @@ namespace Ryujinx.Graphics.Vulkan
_program = internalProgram;
_descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0);
_bindingBarriersDirty = true;
_newState.PipelineLayout = internalProgram.PipelineLayout;
_newState.StagesCount = (uint)stages.Length;
@@ -1066,7 +1068,6 @@ namespace Ryujinx.Graphics.Vulkan
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
{
CreateFramebuffer(colors, depthStencil, filterWriteMasked);
FramebufferParams?.UpdateModifications();
CreateRenderPass();
SignalStateChange();
SignalAttachmentChange();
@@ -1520,8 +1521,18 @@ namespace Ryujinx.Graphics.Vulkan
CreatePipeline(PipelineBindPoint.Compute);
_computeStateDirty = false;
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);
}
@@ -1575,8 +1586,18 @@ namespace Ryujinx.Graphics.Vulkan
_graphicsStateDirty = false;
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);
return true;
@@ -1630,6 +1651,8 @@ namespace Ryujinx.Graphics.Vulkan
{
if (!RenderPassActive)
{
FramebufferParams.InsertLoadOpBarriers(Gd, Cbs);
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
var clearValue = new ClearValue();

View File

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

View File

@@ -111,6 +111,7 @@ namespace Ryujinx.Graphics.Vulkan
bool usePushDescriptors = !isMinimal &&
VulkanConfiguration.UsePushDescriptors &&
_gd.Capabilities.SupportsPushDescriptors &&
!_gd.IsNvidiaPreTuring &&
!IsCompute &&
CanUsePushDescriptors(gd, resourceLayout, IsCompute);

View File

@@ -433,99 +433,65 @@ namespace Ryujinx.Graphics.Vulkan
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
}
public void SetModification(AccessFlags accessFlags, PipelineStageFlags stage)
public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{
_lastModificationAccess = accessFlags;
_lastModificationStage = stage;
}
PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
PipelineStageFlags dstStageFlags = depthStencil ?
PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit :
PipelineStageFlags.ColorAttachmentOutputBit;
public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags, bool insideRenderPass)
{
var lastReadStage = _lastReadStage;
AccessFlags srcAccessFlags = _lastModificationAccess | _lastReadAccess;
AccessFlags dstAccessFlags = depthStencil ?
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.
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
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;
_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;
_lastReadStage |= dstStageFlags;
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();
TextureView.InsertImageBarrier(
_gd.Api,
cbs.CommandBuffer,
_imageAuto.Get(cbs).Value,
_lastModificationAccess,
dstAccessFlags,
_lastModificationStage,
dstStageFlags,
aspectFlags,
0,
0,
_info.GetLayers(),
_info.Levels);
}
else
{
TextureView.InsertMemoryBarrier(
_gd.Api,
cbs.CommandBuffer,
_lastModificationAccess,
dstAccessFlags,
_lastModificationStage,
dstStageFlags);
}
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
_imageAuto.Get(cbs).Value,
_lastModificationAccess,
dstAccessFlags,
aspectFlags,
0,
0,
_info.GetLayers(),
_info.Levels);
_gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
_lastModificationAccess = AccessFlags.None;
}

View File

@@ -497,6 +497,30 @@ namespace Ryujinx.Graphics.Vulkan
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(
Vk api,
CommandBuffer commandBuffer,
@@ -511,18 +535,15 @@ namespace Ryujinx.Graphics.Vulkan
int layers,
int levels)
{
ImageMemoryBarrier memoryBarrier = 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),
};
ImageMemoryBarrier memoryBarrier = GetImageBarrier(
image,
srcAccessMask,
dstAccessMask,
aspectFlags,
firstLayer,
firstLevel,
layers,
levels);
api.CmdPipelineBarrier(
commandBuffer,

View File

@@ -68,6 +68,8 @@ namespace Ryujinx.Graphics.Vulkan
internal HelperShader HelperShader { get; private set; }
internal PipelineFull PipelineInternal => _pipeline;
internal BarrierBatch Barriers { get; private set; }
public IPipeline Pipeline => _pipeline;
public IWindow Window => _window;
@@ -381,6 +383,8 @@ namespace Ryujinx.Graphics.Vulkan
HelperShader = new HelperShader(this, _device);
Barriers = new BarrierBatch(this);
_counters = new Counters(this, _device, _pipeline);
}
@@ -914,6 +918,7 @@ namespace Ryujinx.Graphics.Vulkan
BufferManager.Dispose();
DescriptorSetManager.Dispose();
PipelineLayoutCache.Dispose();
Barriers.Dispose();
MemoryAllocator.Dispose();

View File

@@ -1821,5 +1821,18 @@ namespace Ryujinx.HLE.HOS.Services.Hid
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;
}
}
}