Replace ShaderBindings with new ResourceLayout structure for Vulkan (#5025)

* Introduce ResourceLayout

* Part 1: Use new ResourceSegments array on UpdateAndBind

* Part 2: Use ResourceLayout to build PipelineLayout

* Delete old code

* XML docs

* Fix shader cache load NRE

* Fix typo
This commit is contained in:
gdkchan 2023-05-21 14:04:21 -03:00 committed by GitHub
parent 402f05b8ef
commit 5626f2ca1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1047 additions and 677 deletions

View file

@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
public uint Stages { get; }
public int[][][] Bindings { get; }
public ResourceBindingSegment[][] BindingSegments { get; }
public ProgramLinkStatus LinkStatus { get; private set; }
@ -54,7 +55,13 @@ namespace Ryujinx.Graphics.Vulkan
private Task _compileTask;
private bool _firstBackgroundUse;
public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, SpecDescription[] specDescription = null, bool isMinimal = false)
public ShaderCollection(
VulkanRenderer gd,
Device device,
ShaderSource[] shaders,
ResourceLayout resourceLayout,
SpecDescription[] specDescription = null,
bool isMinimal = false)
{
_gd = gd;
_device = device;
@ -99,39 +106,16 @@ namespace Ryujinx.Graphics.Vulkan
_shaders = internalShaders;
bool usePd = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
_plce = isMinimal
? gd.PipelineLayoutCache.Create(gd, device, shaders)
: gd.PipelineLayoutCache.GetOrCreate(gd, device, stages, usePd);
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors);
HasMinimalLayout = isMinimal;
UsePushDescriptors = usePd;
UsePushDescriptors = usePushDescriptors;
Stages = stages;
int[][] GrabAll(Func<ShaderBindings, IReadOnlyCollection<int>> selector)
{
bool hasAny = false;
int[][] bindings = new int[internalShaders.Length][];
for (int i = 0; i < internalShaders.Length; i++)
{
var collection = selector(internalShaders[i].Bindings);
hasAny |= collection.Count != 0;
bindings[i] = collection.ToArray();
}
return hasAny ? bindings : Array.Empty<int[]>();
}
Bindings = new[]
{
GrabAll(x => x.UniformBufferBindings),
GrabAll(x => x.StorageBufferBindings),
GrabAll(x => x.TextureBindings),
GrabAll(x => x.ImageBindings)
};
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
_compileTask = Task.CompletedTask;
_firstBackgroundUse = false;
@ -141,8 +125,9 @@ namespace Ryujinx.Graphics.Vulkan
VulkanRenderer gd,
Device device,
ShaderSource[] sources,
ResourceLayout resourceLayout,
ProgramPipelineState state,
bool fromCache) : this(gd, device, sources)
bool fromCache) : this(gd, device, sources, resourceLayout)
{
_state = state;
@ -150,6 +135,67 @@ namespace Ryujinx.Graphics.Vulkan
_firstBackgroundUse = !fromCache;
}
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
{
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
{
List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>();
ResourceUsage currentUsage = default;
int currentCount = 0;
for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
{
ResourceUsage usage = setUsages[setIndex].Usages[index];
// If the resource is not accessed, we don't need to update it.
if (usage.Access == ResourceAccess.None)
{
continue;
}
if (currentUsage.Binding + currentCount != usage.Binding ||
currentUsage.Type != usage.Type ||
currentUsage.Stages != usage.Stages ||
currentUsage.Access != usage.Access)
{
if (currentCount != 0)
{
currentSegments.Add(new ResourceBindingSegment(
currentUsage.Binding,
currentCount,
currentUsage.Type,
currentUsage.Stages,
currentUsage.Access));
}
currentUsage = usage;
currentCount = 1;
}
else
{
currentCount++;
}
}
if (currentCount != 0)
{
currentSegments.Add(new ResourceBindingSegment(
currentUsage.Binding,
currentCount,
currentUsage.Type,
currentUsage.Stages,
currentUsage.Access));
}
segments[setIndex] = currentSegments.ToArray();
}
return segments;
}
private async Task BackgroundCompilation()
{
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));