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>
This commit is contained in:
parent
4218311e6a
commit
31ed061bea
18 changed files with 442 additions and 176 deletions
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
225
src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
Normal file
225
src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 VulkanRenderer _gd;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly PipelineBase _pipeline;
|
private readonly PipelineBase _pipeline;
|
||||||
|
@ -42,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;
|
||||||
|
@ -95,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];
|
||||||
|
@ -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()
|
public void AdvancePdSequence()
|
||||||
{
|
{
|
||||||
if (++_pdSequence == 0)
|
if (++_pdSequence == 0)
|
||||||
|
@ -258,7 +313,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_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)
|
||||||
{
|
{
|
||||||
|
@ -267,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;
|
||||||
}
|
}
|
||||||
|
@ -281,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);
|
||||||
}
|
}
|
||||||
|
@ -366,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,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);
|
||||||
}
|
}
|
||||||
|
@ -608,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)
|
||||||
{
|
{
|
||||||
|
@ -645,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]);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -977,6 +978,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_program = internalProgram;
|
_program = internalProgram;
|
||||||
|
|
||||||
_descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0);
|
_descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0);
|
||||||
|
_bindingBarriersDirty = true;
|
||||||
|
|
||||||
_newState.PipelineLayout = internalProgram.PipelineLayout;
|
_newState.PipelineLayout = internalProgram.PipelineLayout;
|
||||||
_newState.StagesCount = (uint)stages.Length;
|
_newState.StagesCount = (uint)stages.Length;
|
||||||
|
@ -1066,7 +1068,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
|
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
|
||||||
{
|
{
|
||||||
CreateFramebuffer(colors, depthStencil, filterWriteMasked);
|
CreateFramebuffer(colors, depthStencil, filterWriteMasked);
|
||||||
FramebufferParams?.UpdateModifications();
|
|
||||||
CreateRenderPass();
|
CreateRenderPass();
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
SignalAttachmentChange();
|
SignalAttachmentChange();
|
||||||
|
@ -1520,8 +1521,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
CreatePipeline(PipelineBindPoint.Compute);
|
CreatePipeline(PipelineBindPoint.Compute);
|
||||||
_computeStateDirty = false;
|
_computeStateDirty = false;
|
||||||
Pbp = PipelineBindPoint.Compute;
|
Pbp = PipelineBindPoint.Compute;
|
||||||
|
|
||||||
|
if (_bindingBarriersDirty)
|
||||||
|
{
|
||||||
|
// Stale barriers may have been activated by switching program. Emit any that are relevant.
|
||||||
|
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
|
||||||
|
|
||||||
|
_bindingBarriersDirty = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
||||||
|
|
||||||
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
|
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1575,8 +1586,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
_graphicsStateDirty = false;
|
_graphicsStateDirty = false;
|
||||||
Pbp = PipelineBindPoint.Graphics;
|
Pbp = PipelineBindPoint.Graphics;
|
||||||
|
|
||||||
|
if (_bindingBarriersDirty)
|
||||||
|
{
|
||||||
|
// Stale barriers may have been activated by switching program. Emit any that are relevant.
|
||||||
|
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
|
||||||
|
|
||||||
|
_bindingBarriersDirty = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
||||||
|
|
||||||
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
|
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1630,6 +1651,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
if (!RenderPassActive)
|
if (!RenderPassActive)
|
||||||
{
|
{
|
||||||
|
FramebufferParams.InsertLoadOpBarriers(Gd, Cbs);
|
||||||
|
|
||||||
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
|
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
|
||||||
var clearValue = new ClearValue();
|
var clearValue = new ClearValue();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -433,99 +433,65 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
|
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetModification(AccessFlags accessFlags, PipelineStageFlags stage)
|
public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
|
||||||
{
|
{
|
||||||
_lastModificationAccess = accessFlags;
|
PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
|
||||||
_lastModificationStage = stage;
|
PipelineStageFlags dstStageFlags = depthStencil ?
|
||||||
}
|
PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit :
|
||||||
|
PipelineStageFlags.ColorAttachmentOutputBit;
|
||||||
|
|
||||||
public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags, bool insideRenderPass)
|
AccessFlags srcAccessFlags = _lastModificationAccess | _lastReadAccess;
|
||||||
{
|
AccessFlags dstAccessFlags = depthStencil ?
|
||||||
var lastReadStage = _lastReadStage;
|
AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.DepthStencilAttachmentReadBit :
|
||||||
|
AccessFlags.ColorAttachmentWriteBit | AccessFlags.ColorAttachmentReadBit;
|
||||||
|
|
||||||
if (insideRenderPass)
|
if (srcAccessFlags != AccessFlags.None)
|
||||||
{
|
{
|
||||||
// We can't have barrier from compute inside a render pass,
|
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
||||||
// as it is invalid to specify compute in the subpass dependency stage mask.
|
ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
|
||||||
|
_imageAuto.Get(cbs).Value,
|
||||||
|
srcAccessFlags,
|
||||||
|
dstAccessFlags,
|
||||||
|
aspectFlags,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
_info.GetLayers(),
|
||||||
|
_info.Levels);
|
||||||
|
|
||||||
lastReadStage &= ~PipelineStageFlags.ComputeShaderBit;
|
_gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags);
|
||||||
}
|
|
||||||
|
|
||||||
if (lastReadStage != PipelineStageFlags.None)
|
|
||||||
{
|
|
||||||
// This would result in a validation error, but is
|
|
||||||
// required on MoltenVK as the generic barrier results in
|
|
||||||
// severe texture flickering in some scenarios.
|
|
||||||
if (_gd.IsMoltenVk)
|
|
||||||
{
|
|
||||||
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
|
||||||
TextureView.InsertImageBarrier(
|
|
||||||
_gd.Api,
|
|
||||||
cbs.CommandBuffer,
|
|
||||||
_imageAuto.Get(cbs).Value,
|
|
||||||
_lastReadAccess,
|
|
||||||
dstAccessFlags,
|
|
||||||
_lastReadStage,
|
|
||||||
dstStageFlags,
|
|
||||||
aspectFlags,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
_info.GetLayers(),
|
|
||||||
_info.Levels);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TextureView.InsertMemoryBarrier(
|
|
||||||
_gd.Api,
|
|
||||||
cbs.CommandBuffer,
|
|
||||||
_lastReadAccess,
|
|
||||||
dstAccessFlags,
|
|
||||||
lastReadStage,
|
|
||||||
dstStageFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastReadAccess = AccessFlags.None;
|
|
||||||
_lastReadStage = PipelineStageFlags.None;
|
_lastReadStage = PipelineStageFlags.None;
|
||||||
|
_lastReadAccess = AccessFlags.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_lastModificationStage = depthStencil ?
|
||||||
|
PipelineStageFlags.LateFragmentTestsBit :
|
||||||
|
PipelineStageFlags.ColorAttachmentOutputBit;
|
||||||
|
|
||||||
|
_lastModificationAccess = depthStencil ?
|
||||||
|
AccessFlags.DepthStencilAttachmentWriteBit :
|
||||||
|
AccessFlags.ColorAttachmentWriteBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
public void QueueWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
||||||
{
|
{
|
||||||
_lastReadAccess |= dstAccessFlags;
|
_lastReadAccess |= dstAccessFlags;
|
||||||
_lastReadStage |= dstStageFlags;
|
_lastReadStage |= dstStageFlags;
|
||||||
|
|
||||||
if (_lastModificationAccess != AccessFlags.None)
|
if (_lastModificationAccess != AccessFlags.None)
|
||||||
{
|
{
|
||||||
// This would result in a validation error, but is
|
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
||||||
// required on MoltenVK as the generic barrier results in
|
ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
|
||||||
// severe texture flickering in some scenarios.
|
_imageAuto.Get(cbs).Value,
|
||||||
if (_gd.IsMoltenVk)
|
_lastModificationAccess,
|
||||||
{
|
dstAccessFlags,
|
||||||
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
aspectFlags,
|
||||||
TextureView.InsertImageBarrier(
|
0,
|
||||||
_gd.Api,
|
0,
|
||||||
cbs.CommandBuffer,
|
_info.GetLayers(),
|
||||||
_imageAuto.Get(cbs).Value,
|
_info.Levels);
|
||||||
_lastModificationAccess,
|
|
||||||
dstAccessFlags,
|
_gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
|
||||||
_lastModificationStage,
|
|
||||||
dstStageFlags,
|
|
||||||
aspectFlags,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
_info.GetLayers(),
|
|
||||||
_info.Levels);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TextureView.InsertMemoryBarrier(
|
|
||||||
_gd.Api,
|
|
||||||
cbs.CommandBuffer,
|
|
||||||
_lastModificationAccess,
|
|
||||||
dstAccessFlags,
|
|
||||||
_lastModificationStage,
|
|
||||||
dstStageFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastModificationAccess = AccessFlags.None;
|
_lastModificationAccess = AccessFlags.None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -381,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -914,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();
|
||||||
|
|
||||||
|
|
Reference in a new issue