[Vk] Refuse push descriptors on macOS for every device through one policy
The guard that refuses push descriptors on MoltenVK keyed on the VK_KHR_portability_subset device extension, but that extension is not enumerated for every device: the headless device the draw replay and the device tests create took the raw driver limit, so replays on macOS read guest storage buffers past their first bytes as zero (MoltenVK reports a zero OpArrayLength for buffers bound through push descriptors). The limit now comes from one policy both the presenter and the headless device use, and macOS, whose only driver is MoltenVK, pushes none regardless of which extensions are listed.
This commit is contained in:
parent
3e878a878f
commit
dbb88bcf22
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Text;
|
||||
using Silk.NET.Vulkan;
|
||||
|
||||
namespace SharpEmu.Libs.Gpu.Vulkan;
|
||||
|
||||
// How many descriptors a set may push. MoltenVK reports a zero OpArrayLength for storage buffers
|
||||
// bound through push descriptors, which turns every bounds-checked load into zero and drops every
|
||||
// store, so portability (MoltenVK) devices push none. The portability-subset extension is not
|
||||
// enumerated for every device, and macOS only ever has MoltenVK, so macOS pushes none outright.
|
||||
// Every device the emulator or its tools create takes the limit from here.
|
||||
public static unsafe class VulkanPushDescriptorPolicy
|
||||
{
|
||||
private const string PortabilitySubsetExtensionName = "VK_KHR_portability_subset";
|
||||
|
||||
public static uint UsableCount(Vk vk, PhysicalDevice physical, uint reported) =>
|
||||
OperatingSystem.IsMacOS() || HasDeviceExtension(vk, physical, PortabilitySubsetExtensionName) ? 0 : reported;
|
||||
|
||||
private static bool HasDeviceExtension(Vk vk, PhysicalDevice physical, string name)
|
||||
{
|
||||
uint count = 0;
|
||||
if (vk.EnumerateDeviceExtensionProperties(physical, (byte*)null, &count, null) != Result.Success || count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var properties = new ExtensionProperties[count];
|
||||
fixed (ExtensionProperties* pointer = properties)
|
||||
{
|
||||
if (vk.EnumerateDeviceExtensionProperties(physical, (byte*)null, &count, pointer) != Result.Success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var expected = Encoding.UTF8.GetBytes(name);
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var actual = new ReadOnlySpan<byte>(pointer[index].ExtensionName, 256);
|
||||
var length = actual.IndexOf((byte)0);
|
||||
if (actual[..(length < 0 ? actual.Length : length)].SequenceEqual(expected))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -594,7 +594,7 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
SetNativeSubgroupCapabilities(subgroup.SubgroupSize, subgroup.SupportedStages);
|
||||
// MoltenVK reports a zero OpArrayLength for storage buffers bound through push
|
||||
// descriptors, which turns every bounds-checked load into zero and drops every store.
|
||||
_maxPushDescriptors = IsDeviceExtensionAvailable(PortabilitySubsetExtensionName) ? 0 : pushDescriptorProperties.MaxPushDescriptors;
|
||||
_maxPushDescriptors = Gpu.Vulkan.VulkanPushDescriptorPolicy.UsableCount(_vk, _physicalDevice, pushDescriptorProperties.MaxPushDescriptors);
|
||||
_noAttachmentSampleCounts = properties.Limits.FramebufferNoAttachmentsSampleCounts;
|
||||
_maxComputeWorkGroupCountX = properties.Limits.MaxComputeWorkGroupCount[0];
|
||||
_maxComputeWorkGroupCountY = properties.Limits.MaxComputeWorkGroupCount[1];
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ internal sealed unsafe class HeadlessVulkan : IDisposable
|
|||
"VK_KHR_push_descriptor",
|
||||
];
|
||||
|
||||
// The push descriptor limit of the device; the render host pushes sets that fit it.
|
||||
// The push descriptor limit the render host may use on this device (none on MoltenVK).
|
||||
public uint MaxPushDescriptors
|
||||
{
|
||||
get
|
||||
|
|
@ -57,7 +57,7 @@ internal sealed unsafe class HeadlessVulkan : IDisposable
|
|||
var pushDescriptors = new PhysicalDevicePushDescriptorPropertiesKHR { SType = StructureType.PhysicalDevicePushDescriptorPropertiesKhr };
|
||||
var properties = new PhysicalDeviceProperties2 { SType = StructureType.PhysicalDeviceProperties2, PNext = &pushDescriptors };
|
||||
Vk.GetPhysicalDeviceProperties2(Physical, &properties);
|
||||
return pushDescriptors.MaxPushDescriptors;
|
||||
return SharpEmu.Libs.Gpu.Vulkan.VulkanPushDescriptorPolicy.UsableCount(Vk, Physical, pushDescriptors.MaxPushDescriptors);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue