diff --git a/docs/releases/v0.10.2.md b/docs/releases/v0.10.2.md index 79f13975..7ce74ccd 100644 --- a/docs/releases/v0.10.2.md +++ b/docs/releases/v0.10.2.md @@ -36,6 +36,10 @@ cards). - **Sky-coloured holes in the ground where grass is still loading**, on cards that take the mesh shader fallback: a patch of grass or leaves is now left out whole until it is ready, where its depth alone was drawn before (`mesh_fallback_hold = 0` brings that back). Not yet confirmed by players with these cards either. +- **The game not starting after a settings change on a laptop with two graphics cards** ("no usable Vulkan device", + code 70, until nx2navi.ini was edited by hand): with DLSS Frame Generation on and the *Graphics card* setting on + the card that is not NVIDIA's, the device could not be made. The game now starts on the NVIDIA card, and where + there is none, or frame generation cannot be set up, it starts without frame generation and the log says why. - **Crashes and hangs found in a review of the whole runtime**; none was seen in our own play, and each fix has a check of its own: - a crash when the game read memory the lazy write-back was just handing to the GPU (NVIDIA and Intel cards); diff --git a/runtime/vk_device.c b/runtime/vk_device.c index afc29a87..494b03e9 100644 --- a/runtime/vk_device.c +++ b/runtime/vk_device.c @@ -268,6 +268,14 @@ retry: VK_API_VERSION_MAJOR(p.apiVersion), VK_API_VERSION_MINOR(p.apiVersion), VK_API_VERSION_PATCH(p.apiVersion)); if (want >= 0 && (int)i != want) continue; if (name && *name && !name_has(p.deviceName, name)) continue; + // A chosen card of another vendor while DLSS Frame Generation is on: the interposer cannot make its device + // (vk_dlssg_create_device), and the settings ask for what only an NVIDIA card runs. Not usable, so the best + // usable one is taken below, as for a card that is gone; where no NVIDIA card is usable either, the pass + // without a choice takes what there is and the device is made without frame generation. + if ((want >= 0 || (name && *name)) && vk_dlssg_interposed() && p.vendorID != 0x10de) { + rt_log("vk: gpu %u skipped: DLSS Frame Generation is on (framegen = dlss), which only an NVIDIA card runs", i); + continue; + } if (p.apiVersion < VK_API_VERSION_1_3) { rt_log("vk: gpu %u skipped: Vulkan below 1.3", i); continue; } const char *miss = missing_required(pd[i]); if (miss) { rt_log("vk: gpu %u skipped: missing extension %s", i, miss); continue; } diff --git a/runtime/vk_dlssg.c b/runtime/vk_dlssg.c index d8f93fd7..51812434 100644 --- a/runtime/vk_dlssg.c +++ b/runtime/vk_dlssg.c @@ -199,9 +199,34 @@ VkResult vk_dlssg_enumerate(VkInstance inst, uint32_t *n, VkPhysicalDevice *pd) return f ? f(inst, n, pd) : vkEnumeratePhysicalDevices(inst, n, pd); } +int vk_dlssg_interposed(void) { return S.loaded > 0; } + +// Frame generation is off for the run from here on: every later call goes to the loader alone, as when Streamline's +// files are missing, and the settings menu says it is unavailable (vk_dlssg_status). +static void give_up(const char *why) { + S.loaded = -1, S.fail = DLSSG_UNAVAILABLE; + rt_log("dlssg: DLSS Frame Generation is off in this run: %s", why); +} + +// The interposer's vkCreateDevice asks the device for NVIDIA's extensions (VK_NV_low_latency2 among them) and fails +// without them (VK_ERROR_EXTENSION_NOT_PRESENT). On a laptop with an NVIDIA card and Intel graphics, frame generation +// set to DLSS and the Graphics card setting on the Intel one, that ended every start with "no usable Vulkan device" +// (exit code 70) until nx2navi.ini was edited by hand: four reports on 0.10.1. A device of another vendor is now made +// by the loader, without frame generation, and so is one the interposer refuses for any other reason. VkResult vk_dlssg_create_device(VkInstance inst, VkPhysicalDevice pd, const VkDeviceCreateInfo *ci, VkDevice *out) { + if (S.loaded > 0) { + VkPhysicalDeviceProperties p; + vkGetPhysicalDeviceProperties(pd, &p); + if (p.vendorID != 0x10de) give_up("the graphics card in use is not an NVIDIA card (the Graphics card setting, gpu_index)"); + } PFN_vkCreateDevice f = S.loaded > 0 ? (PFN_vkCreateDevice)S.gipa()(inst, "vkCreateDevice") : NULL; - return f ? f(pd, ci, NULL, out) : vkCreateDevice(pd, ci, NULL, out); + if (!f) return vkCreateDevice(pd, ci, NULL, out); + VkResult r = f(pd, ci, NULL, out); + if (r == VK_SUCCESS) return r; + char why[96]; + snprintf(why, sizeof why, "Streamline could not make the device (%d)", (int)r); + give_up(why); + return vkCreateDevice(pd, ci, NULL, out); } void vk_dlssg_device_ready(VkPhysicalDevice physical, VkDevice dev) { diff --git a/runtime/vk_dlssg.h b/runtime/vk_dlssg.h index 895fa584..6af9bc0c 100644 --- a/runtime/vk_dlssg.h +++ b/runtime/vk_dlssg.h @@ -9,6 +9,9 @@ int vk_dlssg_early(void); // before the instance exists: loads the DLL and St VkResult vk_dlssg_create_instance(const VkInstanceCreateInfo *ci, VkInstance *out); VkResult vk_dlssg_enumerate(VkInstance inst, uint32_t *n, VkPhysicalDevice *pd); // through the interposer too PFN_vkVoidFunction vk_dlssg_instance_proc(VkInstance inst, const char *name); // the interposer's, or NULL +int vk_dlssg_interposed(void); // Streamline's proxies are in use (until a device is made without them) +// The device through the interposer; by the loader alone, with frame generation off for the run, when the card is not +// an NVIDIA one or the interposer fails to make it. VkResult vk_dlssg_create_device(VkInstance inst, VkPhysicalDevice pd, const VkDeviceCreateInfo *ci, VkDevice *out); void vk_dlssg_device_ready(VkPhysicalDevice physical, VkDevice dev); // after the device: checks DLSS-G and takes the swapchain hooks PFN_vkGetDeviceProcAddr vk_dlssg_dpa(void); // Streamline's device entry points, or NULL when it is not in use