v1.2.2: stability update. Aims at a DLSS startup crash reported on some NVIDIA GPUs, where device creation rejected the dedicated optical-flow queue and the emulator closed a few seconds after launching a game. It now continues without that queue instead of closing, so DLSS and Frame Generation keep working; the optical-flow accelerator falls back to software. We could not reproduce it on our own hardware, so this is built from user logs, and the build records the optical-flow queue family details to help confirm. All opt-in, stock behavior by default.
This commit is contained in:
@@ -302,6 +302,20 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
if (i != queueFamilyIndex && ((uint)physicalDevice.QueueFamilyProperties[i].QueueFlags & 0x100u) != 0)
|
||||
{
|
||||
opticalFlowQueueFamilyIndex = i;
|
||||
|
||||
// Diagnostic (NVOFA): dump the chosen optical-flow family's properties. When a user's
|
||||
// vkCreateDevice REJECTS this dedicated queue (the INITIALIZATION_FAILED some NVIDIA
|
||||
// configs hit), their log then shows WHAT is different about their OFA family
|
||||
// (queueCount / flags / timestampValidBits) versus a working one -- the lever for a
|
||||
// real fix instead of the queue-drop fallback. One Info line; the driver version is
|
||||
// printed by PrintGpuInformation right after.
|
||||
QueueFamilyProperties ofaProps = physicalDevice.QueueFamilyProperties[i];
|
||||
QueueFamilyProperties gfxProps = physicalDevice.QueueFamilyProperties[queueFamilyIndex];
|
||||
Logger.Info?.Print(LogClass.Gpu,
|
||||
$"NVOFA: optical-flow queue family {i} selected -- queueCount={ofaProps.QueueCount}, " +
|
||||
$"flags=0x{(uint)ofaProps.QueueFlags:X}, timestampValidBits={ofaProps.TimestampValidBits}; " +
|
||||
$"graphics family {queueFamilyIndex} queueCount={gfxProps.QueueCount}; total families={physicalDevice.QueueFamilyProperties.Length}.");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -423,6 +437,29 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout;
|
||||
}
|
||||
|
||||
PhysicalDeviceSynchronization2Features supportedFeaturesSync2 = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceSynchronization2Features,
|
||||
};
|
||||
|
||||
PhysicalDeviceOpticalFlowFeaturesNV supportedFeaturesOpticalFlow = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceOpticalFlowFeaturesNV,
|
||||
};
|
||||
|
||||
// Query support for the NVOFA (optical flow) features, so we only ENABLE them below when the
|
||||
// device actually advertises them. Every other feature block already does this; this pair was
|
||||
// the lone exception. Chained only when DLSS is on and the extension is present, so non-DLSS
|
||||
// device creation is left unchanged.
|
||||
if (Dlss.DlssIntegration.IsEnabled && physicalDevice.IsDeviceExtensionPresent("VK_NV_optical_flow"))
|
||||
{
|
||||
supportedFeaturesSync2.PNext = features2.PNext;
|
||||
features2.PNext = &supportedFeaturesSync2;
|
||||
|
||||
supportedFeaturesOpticalFlow.PNext = features2.PNext;
|
||||
features2.PNext = &supportedFeaturesOpticalFlow;
|
||||
}
|
||||
|
||||
PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceVulkan12Features,
|
||||
@@ -637,14 +674,47 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout;
|
||||
}
|
||||
|
||||
// NVOFA (B2): the DEDICATED optical-flow queue (a 2nd VkDeviceQueue on the NVIDIA OFA family)
|
||||
// is OUR OWN addition, used only by NvOpticalFlow for hardware motion vectors. Some NVIDIA
|
||||
// GPU/driver combos reject a device created with it (VK_ERROR_INITIALIZATION_FAILED at
|
||||
// vkCreateDevice), which killed the renderer at startup for users who turned DLSS on. We now
|
||||
// (a) enable the OFA / sync2 FEATURES only when the device reports them supported (queried
|
||||
// above), and (b) if creation WITH the dedicated queue fails, retry WITHOUT that queue (see the
|
||||
// device-creation block below). The extension and features stay enabled either way: Streamline's
|
||||
// DLSS-G (Frame Generation) needs them and submits its own optical flow on the GRAPHICS queue,
|
||||
// not ours; and NvOpticalFlow falls back to its Lucas-Kanade estimator on its own when the queue
|
||||
// is absent (its _queueReady stays false). RYUJINX_DLSS_NO_OFA forces the no-queue path so it can
|
||||
// be validated on hardware that does NOT crash (e.g. an RTX 5090).
|
||||
bool forceNoOpticalFlowQueue =
|
||||
Environment.GetEnvironmentVariable("RYUJINX_DLSS_NO_OFA") is "1" or "true" or "on";
|
||||
|
||||
bool opticalFlowExtPresent =
|
||||
Dlss.DlssIntegration.IsEnabled && physicalDevice.IsDeviceExtensionPresent("VK_NV_optical_flow");
|
||||
bool sync2Supported = opticalFlowExtPresent && supportedFeaturesSync2.Synchronization2;
|
||||
bool opticalFlowSupported = opticalFlowExtPresent && supportedFeaturesOpticalFlow.OpticalFlow;
|
||||
|
||||
// Whether to request OUR dedicated OFA queue. Gated on support + a distinct OFA family existing
|
||||
// + not forced off. If we won't use it, drop the family index so the caller skips the queue.
|
||||
bool useOpticalFlowQueue =
|
||||
!forceNoOpticalFlowQueue && opticalFlowQueueFamilyIndex != uint.MaxValue && sync2Supported && opticalFlowSupported;
|
||||
|
||||
if (!useOpticalFlowQueue && opticalFlowQueueFamilyIndex != uint.MaxValue)
|
||||
{
|
||||
string why = forceNoOpticalFlowQueue ? "forced off (RYUJINX_DLSS_NO_OFA)" :
|
||||
!sync2Supported ? "synchronization2 feature not supported" :
|
||||
"opticalFlow feature not supported";
|
||||
Logger.Info?.Print(LogClass.Gpu, $"NVOFA: dedicated optical-flow queue not requested ({why}); DLSS/FG unaffected, NvOpticalFlow uses Lucas-Kanade.");
|
||||
opticalFlowQueueFamilyIndex = uint.MaxValue;
|
||||
}
|
||||
|
||||
PhysicalDeviceSynchronization2Features featuresSync2;
|
||||
PhysicalDeviceOpticalFlowFeaturesNV featuresOpticalFlow;
|
||||
|
||||
if (Dlss.DlssIntegration.IsEnabled && physicalDevice.IsDeviceExtensionPresent("VK_NV_optical_flow"))
|
||||
// Enable the OFA / sync2 features whenever the device supports them -- KEPT even if we drop our
|
||||
// dedicated queue, because Streamline's DLSS-G relies on them. synchronization2 is also a hard
|
||||
// dependency of VK_NV_optical_flow.
|
||||
if (sync2Supported)
|
||||
{
|
||||
// NVOFA (B2): VK_NV_optical_flow needs synchronization2 as a dependency. Enable both
|
||||
// features, only when DLSS is on and the device advertises the extension, so the default
|
||||
// (non-DLSS) device creation is left byte-identical to upstream.
|
||||
featuresSync2 = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceSynchronization2Features,
|
||||
@@ -653,7 +723,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
};
|
||||
|
||||
pExtendedFeatures = &featuresSync2;
|
||||
}
|
||||
|
||||
if (opticalFlowSupported)
|
||||
{
|
||||
featuresOpticalFlow = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceOpticalFlowFeaturesNV,
|
||||
@@ -681,24 +754,44 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
ppEnabledExtensions[i] = Marshal.StringToHGlobalAnsi(enabledExtensions[i]);
|
||||
}
|
||||
|
||||
// The extensions and the feature (pNext) chain are IDENTICAL across both attempts; only our
|
||||
// dedicated OFA queue (the 2nd DeviceQueueCreateInfo) is dropped on the fallback attempt.
|
||||
DeviceCreateInfo deviceCreateInfo = new()
|
||||
{
|
||||
SType = StructureType.DeviceCreateInfo,
|
||||
PNext = pExtendedFeatures,
|
||||
QueueCreateInfoCount = queueCreateInfoCount,
|
||||
QueueCreateInfoCount = useOpticalFlowQueue ? 2u : 1u,
|
||||
PQueueCreateInfos = queueCreateInfos,
|
||||
PpEnabledExtensionNames = (byte**)ppEnabledExtensions,
|
||||
EnabledExtensionCount = (uint)enabledExtensions.Length,
|
||||
PEnabledFeatures = &features,
|
||||
};
|
||||
|
||||
api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out Device device).ThrowOnError();
|
||||
Result result = api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out Device device);
|
||||
|
||||
if (result != Result.Success && useOpticalFlowQueue)
|
||||
{
|
||||
// Some NVIDIA GPU/driver combos reject the device when our dedicated optical-flow queue is
|
||||
// requested (seen as VK_ERROR_INITIALIZATION_FAILED). Retry WITHOUT that queue so DLSS -- and
|
||||
// Streamline's Frame Generation, which uses the graphics queue and keeps the extension --
|
||||
// still run; NvOpticalFlow falls back to Lucas-Kanade. The result code names the reason in
|
||||
// the affected user's log.
|
||||
Logger.Warning?.Print(LogClass.Gpu,
|
||||
$"NVOFA: device creation with the dedicated optical-flow queue failed ({result}); retrying without it. " +
|
||||
"DLSS and Frame Generation stay on; hardware optical flow (NvOpticalFlow) is disabled.");
|
||||
|
||||
opticalFlowQueueFamilyIndex = uint.MaxValue;
|
||||
deviceCreateInfo.QueueCreateInfoCount = 1;
|
||||
result = api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out device);
|
||||
}
|
||||
|
||||
for (int i = 0; i < enabledExtensions.Length; i++)
|
||||
{
|
||||
Marshal.FreeHGlobal(ppEnabledExtensions[i]);
|
||||
}
|
||||
|
||||
result.ThrowOnError();
|
||||
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user