From cfc75d7e78a63fe3bde06b6e4896a42e8dedaf82 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sun, 7 Jul 2024 19:19:55 -0300
Subject: Disable descriptor set template updates for buffer textures on Adreno
 (#7002)

* Do not use template updates for buffer textures and buffer images

* No need to do it for images

* Simply buffer texture existence check

* Pipeline is now unused on DescriptorSetUpdater
---
 .../DescriptorSetUpdater.cs                        | 98 +++++++++++++++++-----
 src/Ryujinx.Graphics.Vulkan/PipelineBase.cs        |  2 +-
 src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs    | 16 +++-
 3 files changed, 93 insertions(+), 23 deletions(-)

diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
index 3590d5d0..75ffca2c 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
@@ -73,7 +73,6 @@ namespace Ryujinx.Graphics.Vulkan
 
         private readonly VulkanRenderer _gd;
         private readonly Device _device;
-        private readonly PipelineBase _pipeline;
         private ShaderCollection _program;
 
         private readonly BufferRef[] _uniformBufferRefs;
@@ -125,11 +124,10 @@ namespace Ryujinx.Graphics.Vulkan
         private readonly TextureView _dummyTexture;
         private readonly SamplerHolder _dummySampler;
 
-        public DescriptorSetUpdater(VulkanRenderer gd, Device device, PipelineBase pipeline)
+        public DescriptorSetUpdater(VulkanRenderer gd, Device device)
         {
             _gd = gd;
             _device = device;
-            _pipeline = pipeline;
 
             // Some of the bindings counts needs to be multiplied by 2 because we have buffer and
             // regular textures/images interleaved on the same descriptor set.
@@ -684,7 +682,14 @@ namespace Ryujinx.Graphics.Vulkan
 
             if (_dirty.HasFlag(DirtyFlags.Texture))
             {
-                UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
+                if (program.UpdateTexturesWithoutTemplate)
+                {
+                    UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
+                }
+                else
+                {
+                    UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
+                }
             }
 
             if (_dirty.HasFlag(DirtyFlags.Image))
@@ -918,31 +923,84 @@ namespace Ryujinx.Graphics.Vulkan
             _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
         }
 
-        private unsafe void UpdateBuffers(
-            CommandBufferScoped cbs,
-            PipelineBindPoint pbp,
-            int baseBinding,
-            ReadOnlySpan<DescriptorBufferInfo> bufferInfo,
-            DescriptorType type)
+        private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
         {
-            if (bufferInfo.Length == 0)
+            int setIndex = PipelineBase.TextureSetIndex;
+            var bindingSegments = program.BindingSegments[setIndex];
+
+            if (bindingSegments.Length == 0)
             {
                 return;
             }
 
-            fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
+            if (_updateDescriptorCacheCbIndex)
             {
-                var writeDescriptorSet = new WriteDescriptorSet
+                _updateDescriptorCacheCbIndex = false;
+                program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
+            }
+
+            var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs);
+
+            foreach (ResourceBindingSegment segment in bindingSegments)
+            {
+                int binding = segment.Binding;
+                int count = segment.Count;
+
+                if (!segment.IsArray)
                 {
-                    SType = StructureType.WriteDescriptorSet,
-                    DstBinding = (uint)baseBinding,
-                    DescriptorType = type,
-                    DescriptorCount = (uint)bufferInfo.Length,
-                    PBufferInfo = pBufferInfo,
-                };
+                    if (segment.Type != ResourceType.BufferTexture)
+                    {
+                        Span<DescriptorImageInfo> textures = _textures;
+
+                        for (int i = 0; i < count; i++)
+                        {
+                            ref var texture = ref textures[i];
+                            ref var refs = ref _textureRefs[binding + i];
 
-                _gd.PushDescriptorApi.CmdPushDescriptorSet(cbs.CommandBuffer, pbp, _program.PipelineLayout, 0, 1, &writeDescriptorSet);
+                            texture.ImageView = refs.View?.Get(cbs).Value ?? default;
+                            texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
+
+                            if (texture.ImageView.Handle == 0)
+                            {
+                                texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
+                            }
+
+                            if (texture.Sampler.Handle == 0)
+                            {
+                                texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
+                            }
+                        }
+
+                        dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler);
+                    }
+                    else
+                    {
+                        Span<BufferView> bufferTextures = _bufferTextures;
+
+                        for (int i = 0; i < count; i++)
+                        {
+                            bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
+                        }
+
+                        dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer);
+                    }
+                }
+                else
+                {
+                    if (segment.Type != ResourceType.BufferTexture)
+                    {
+                        dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler);
+                    }
+                    else
+                    {
+                        dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer);
+                    }
+                }
             }
+
+            var sets = dsc.GetSets();
+
+            _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 918de59b..8d7cd547 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -105,7 +105,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
 
-            _descriptorSetUpdater = new DescriptorSetUpdater(gd, device, this);
+            _descriptorSetUpdater = new DescriptorSetUpdater(gd, device);
             _vertexBufferUpdater = new VertexBufferUpdater(gd);
 
             _transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
index f9637789..eec2a318 100644
--- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
         public bool IsCompute { get; }
         public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0;
 
+        public bool UpdateTexturesWithoutTemplate { get; }
+
         public uint Stages { get; }
 
         public ResourceBindingSegment[][] ClearSegments { get; }
@@ -127,9 +129,12 @@ namespace Ryujinx.Graphics.Vulkan
             Stages = stages;
 
             ClearSegments = BuildClearSegments(sets);
-            BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
+            BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
             Templates = BuildTemplates(usePushDescriptors);
 
+            // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
+            UpdateTexturesWithoutTemplate = gd.Vendor == Vendor.Qualcomm && usesBufferTextures;
+
             _compileTask = Task.CompletedTask;
             _firstBackgroundUse = false;
         }
@@ -280,8 +285,10 @@ namespace Ryujinx.Graphics.Vulkan
             return segments;
         }
 
-        private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
+        private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages, out bool usesBufferTextures)
         {
+            usesBufferTextures = false;
+
             ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
 
             for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
@@ -295,6 +302,11 @@ namespace Ryujinx.Graphics.Vulkan
                 {
                     ResourceUsage usage = setUsages[setIndex].Usages[index];
 
+                    if (usage.Type == ResourceType.BufferTexture)
+                    {
+                        usesBufferTextures = true;
+                    }
+
                     if (currentUsage.Binding + currentCount != usage.Binding ||
                         currentUsage.Type != usage.Type ||
                         currentUsage.Stages != usage.Stages ||
-- 
cgit v1.2.3-70-g09d2