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.
This commit is contained in:
Foued Attar 2026-10-12 20:18:36 +02:00 committed by GitHub
parent 7ebf642aff
commit 682ad298ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 5 additions and 16 deletions

View File

@ -1474,6 +1474,7 @@ internal static unsafe partial class VulkanVideoPresenter
CreateScheduler();
CreateBufferCache();
CreateImageCache();
CreateBindlessImageHeap();
_descriptorHeap = new DescriptorHeap(_deviceInfo, _scheduler);
CreateCommandStream();
LoadDebugUtilsCommands();

View File

@ -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<DescriptorSetLayoutBinding>();
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<DescriptorSetLayoutBinding>();
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)

View File

@ -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(