From 682ad298efa82feab84c7b47e84dfe9d623b5ce3 Mon Sep 17 00:00:00 2001 From: Foued Attar Date: Mon, 12 Oct 2026 20:18:36 +0200 Subject: [PATCH] fix(vk): create the bindless image heap at device setup (#1088) The heap was created lazily by pipeline creation and by the shader prewarm, but a draw binds its bindless slot table before it creates its pipeline. A title whose first draw samples images, with no pending prewarm work, hit a null heap in BindFlattenedResourceTable. The heap depends only on a device capability, so create it with the stores and drop the lazy calls. --- .../VulkanVideoPresenter.Device.Setup.cs | 1 + .../VideoOut/VulkanVideoPresenter.Pipelines.cs | 17 ++++------------- .../VulkanVideoPresenter.ShaderPrewarm.cs | 3 --- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.Device.Setup.cs b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.Device.Setup.cs index b82bbb5..9565274 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.Device.Setup.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.Device.Setup.cs @@ -1474,6 +1474,7 @@ internal static unsafe partial class VulkanVideoPresenter CreateScheduler(); CreateBufferCache(); CreateImageCache(); + CreateBindlessImageHeap(); _descriptorHeap = new DescriptorHeap(_deviceInfo, _scheduler); CreateCommandStream(); LoadDebugUtilsCommands(); diff --git a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.Pipelines.cs b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.Pipelines.cs index a3e42ab..b1b1d46 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.Pipelines.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.Pipelines.cs @@ -622,17 +622,11 @@ internal static unsafe partial class VulkanVideoPresenter } } - private void EnsureBindlessImageHeap(params ShaderProgramInfo?[] programs) - => EnsureBindlessImageHeapForMode(programs.Any(program => program?.Bindings?.UsesBindlessImages == true)); - - private void EnsureBindlessImageHeapForMode(bool usesBindlessImages) + // Every translated program samples through this one set when the device supports it, + // and a draw binds its slots before it creates its pipeline. Create it with the stores. + private void CreateBindlessImageHeap() { - if (_bindlessImageHeap is not null) - { - return; - } - - if (!usesBindlessImages) + if (!BindlessImageHeapEnabled) { return; } @@ -730,7 +724,6 @@ internal static unsafe partial class VulkanVideoPresenter throw SubmissionScheduler.Fatal($"The draw binds more color attachments than the device supports: count={description.Rendering.ColorCount} max={_maxColorAttachments}."); } - EnsureBindlessImageHeap(description.VertexStage, description.PixelStage); var usesBindlessImages = description.VertexStage.Bindings!.UsesBindlessImages || description.PixelStage?.Bindings?.UsesBindlessImages == true; @@ -1197,7 +1190,6 @@ internal static unsafe partial class VulkanVideoPresenter public PipelineHandle CreateComputePipeline(ComputePipelineDescription description) { using var profileScope = RenderPhaseProfile.MeasureDetail(RenderPhaseProfile.Phase.PipelineSetup); - EnsureBindlessImageHeap(description.Stage); var bindings = new List(); CollectLayoutBindings(bindings, description.Stage, ShaderStage.Compute); var setLayout = CreateDescriptorSetLayout(bindings, out var usesPushDescriptors, out var demand); @@ -1336,7 +1328,6 @@ internal static unsafe partial class VulkanVideoPresenter var bindings = new List(); CollectLayoutBindings(bindings, description.Stage, ShaderStage.Compute); var setLayout = CreateDescriptorSetLayout(bindings, out var usesPushDescriptors, out var demand); - EnsureBindlessImageHeap(description.Stage); var layout = CreatePipelineLayout(setLayout, ShaderStageFlags.ComputeBit, description.Stage.Bindings!.UsesBindlessImages); var computeModule = new ShaderModule(description.Program.Module); if (computeModule.Handle == 0) diff --git a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.ShaderPrewarm.cs b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.ShaderPrewarm.cs index c9e8a8a..d9dd75b 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.ShaderPrewarm.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.ShaderPrewarm.cs @@ -93,9 +93,6 @@ internal static unsafe partial class VulkanVideoPresenter var workerCount = Math.Clamp(Environment.ProcessorCount / 4, 1, 4); var remaining = workerCount; var host = (IShaderPipelineHost)this; - // The workers create layouts that include the global set. Initialize - // it once on the device setup thread, before any worker can use it. - EnsureBindlessImageHeapForMode(host.UsesBindlessImages); Volatile.Write(ref _shaderPrewarmTotal, pending.Count); Volatile.Write(ref _shaderPrewarmProgress, 0); Console.Error.WriteLine(