From 8a12c113f9d6ec19795e8ebc10c0794031f228ca Mon Sep 17 00:00:00 2001 From: Gabriel A Date: Sat, 6 Jul 2024 14:34:06 -0300 Subject: [PATCH] Do not use template updates for buffer textures and buffer images --- .../DescriptorSetUpdater.cs | 164 +++++++++++++++++- .../ShaderCollection.cs | 23 +++ 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 3590d5d057..29b0e2d4af 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -684,12 +684,26 @@ 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)) { - UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp); + if (program.UpdateImagesWithoutTemplate) + { + UpdateAndBindImagesWithoutTemplate(cbs, program, pbp); + } + else + { + UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp); + } } if (program.BindingSegments.Length > PipelineBase.DescriptorSetLayouts) @@ -918,6 +932,152 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); } + private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp) + { + int setIndex = PipelineBase.TextureSetIndex; + var bindingSegments = program.BindingSegments[setIndex]; + + if (bindingSegments.Length == 0) + { + return; + } + + if (_updateDescriptorCacheCbIndex) + { + _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) + { + if (segment.Type != ResourceType.BufferTexture) + { + Span textures = _textures; + + for (int i = 0; i < count; i++) + { + ref var texture = ref textures[i]; + ref var refs = ref _textureRefs[binding + i]; + + 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 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.Empty); + } + + private void UpdateAndBindImagesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp) + { + int setIndex = PipelineBase.ImageSetIndex; + var bindingSegments = program.BindingSegments[setIndex]; + + if (bindingSegments.Length == 0) + { + return; + } + + if (_updateDescriptorCacheCbIndex) + { + _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) + { + if (segment.Type != ResourceType.BufferImage) + { + Span images = _images; + + for (int i = 0; i < count; i++) + { + images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default; + } + + dsc.UpdateImages(0, binding, images[..count], DescriptorType.StorageImage); + } + else + { + Span bufferImages = _bufferImages; + + for (int i = 0; i < count; i++) + { + bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default; + } + + dsc.UpdateBufferImages(0, binding, bufferImages[..count], DescriptorType.StorageTexelBuffer); + } + } + else + { + if (segment.Type != ResourceType.BufferTexture) + { + dsc.UpdateImages(0, binding, _imageArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture), DescriptorType.StorageImage); + } + else + { + dsc.UpdateBufferImages(0, binding, _imageArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.StorageTexelBuffer); + } + } + } + + var sets = dsc.GetSets(); + + _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); + } + private unsafe void UpdateBuffers( CommandBufferScoped cbs, PipelineBindPoint pbp, diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index f9637789e3..3e287e1ac6 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -23,6 +23,9 @@ namespace Ryujinx.Graphics.Vulkan public bool IsCompute { get; } public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0; + public bool UpdateTexturesWithoutTemplate { get; } + public bool UpdateImagesWithoutTemplate { get; } + public uint Stages { get; } public ResourceBindingSegment[][] ClearSegments { get; } @@ -130,6 +133,26 @@ namespace Ryujinx.Graphics.Vulkan BindingSegments = BuildBindingSegments(resourceLayout.SetUsages); Templates = BuildTemplates(usePushDescriptors); + if (gd.Vendor == Vendor.Qualcomm) + { + // Updating buffer bindings using template updates crashes the Adreno driver on Windows. + + foreach (ResourceBindingSegment[] stageSegments in BindingSegments) + { + foreach (ResourceBindingSegment bindingSegment in stageSegments) + { + if (bindingSegment.Type == ResourceType.BufferTexture) + { + UpdateTexturesWithoutTemplate = true; + } + else if (bindingSegment.Type == ResourceType.BufferImage) + { + UpdateImagesWithoutTemplate = true; + } + } + } + } + _compileTask = Task.CompletedTask; _firstBackgroundUse = false; }