From ad8f44b78a455556828dc0a64920d1e363e1092f Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 15 Sep 2026 19:05:27 +0300 Subject: [PATCH] feat(vk): load NVIDIA Streamline's interposer as the Vulkan loader on request vk_sl_prefer(1) before Vk.open() makes the Windows loader try sl.interposer.dll beside the executable and fall back to vulkan-1.dll. Thunks for the sl* exports and for calling feature functions from slGetFeatureFunction; macOS stubs. Verified on an RTX 3070 Ti: slInit eOk, DLSS, DLSS-RR, Reflex and PCL supported, DLSS-G reports no supported adapter (needs RTX 40). Co-Authored-By: Claude Opus 5 --- runtime/native/vk.ludic | 24 ++++ runtime/native/vk_mac.ll | 74 +++++++++++ runtime/native/vk_win.ll | 264 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 362 insertions(+) diff --git a/runtime/native/vk.ludic b/runtime/native/vk.ludic index dfb9b1e3..cd352a15 100644 --- a/runtime/native/vk.ludic +++ b/runtime/native/vk.ludic @@ -19,3 +19,27 @@ # ============================================================================ import "vk_api.ludic" + +# Streamline (NVIDIA DLSS, Ray Reconstruction, Frame Generation, Reflex), Windows only: Vk.sl_prefer(1) +# before Vk.open() loads sl.interposer.dll as the Vulkan loader when it is beside the program, and +# Vk.sl_active() says whether it was. Each Vk.sl_* is the sl* export of the same name and returns +# its sl::Result (0 is eOk); -1 means there is no Streamline (macOS, or the DLL missing). The +# structs are plain memory, laid out as the SDK headers (include/sl_*.h, SDK 2.14.1) declare them. +extern function vk_sl_prefer(on: int) = "lvk_sl_prefer" +extern function vk_sl_active() -> int = "lvk_sl_active" +extern function vk_sl_init(pref: pointer, sdk_version: long) -> int = "lsl_slInit" +extern function vk_sl_shutdown() -> int = "lsl_slShutdown" +extern function vk_sl_is_feature_supported(feature: int, adapter_info: pointer) -> int = "lsl_slIsFeatureSupported" +extern function vk_sl_is_feature_loaded(feature: int, loaded: pointer) -> int = "lsl_slIsFeatureLoaded" +extern function vk_sl_get_feature_requirements(feature: int, requirements: pointer) -> int = "lsl_slGetFeatureRequirements" +extern function vk_sl_get_feature_function(feature: int, name: pointer, function_out: pointer) -> int = "lsl_slGetFeatureFunction" +extern function vk_sl_get_new_frame_token(token_out: pointer, frame_index: pointer) -> int = "lsl_slGetNewFrameToken" +extern function vk_sl_set_tag_for_frame(frame: pointer, viewport: pointer, tags: pointer, num_tags: int, cmd_buffer: pointer) -> int = "lsl_slSetTagForFrame" +extern function vk_sl_set_constants(constants: pointer, frame: pointer, viewport: pointer) -> int = "lsl_slSetConstants" +extern function vk_sl_evaluate_feature(feature: int, frame: pointer, inputs: pointer, num_inputs: int, cmd_buffer: pointer) -> int = "lsl_slEvaluateFeature" +extern function vk_sl_free_resources(feature: int, viewport: pointer) -> int = "lsl_slFreeResources" +# a feature function obtained with Vk.sl_get_feature_function, called with its arguments +extern function vk_sl_call_p(fn: pointer, a: pointer) -> int = "lsl_call_p" +extern function vk_sl_call_pp(fn: pointer, a: pointer, b: pointer) -> int = "lsl_call_pp" +extern function vk_sl_call_ppp(fn: pointer, a: pointer, b: pointer, c: pointer) -> int = "lsl_call_ppp" +extern function vk_sl_call_ip(fn: pointer, a: int, b: pointer) -> int = "lsl_call_ip" diff --git a/runtime/native/vk_mac.ll b/runtime/native/vk_mac.ll index abd1abce..70e3b19d 100644 --- a/runtime/native/vk_mac.ll +++ b/runtime/native/vk_mac.ll @@ -108,3 +108,77 @@ entry: %r = zext i1 %there to i32 ret i32 %r } + +; ---- Streamline: Windows only. The same symbols, so a program that calls them links everywhere. ---- +define void @lvk_sl_prefer(i32 %on) { +entry: + ret void +} +define i32 @lvk_sl_active() { +entry: + ret i32 0 +} +define i32 @lsl_slInit(ptr %a0, i64 %a1) { +entry: + ret i32 -1 +} +define i32 @lsl_slShutdown() { +entry: + ret i32 -1 +} +define i32 @lsl_slIsFeatureSupported(i32 %a0, ptr %a1) { +entry: + ret i32 -1 +} +define i32 @lsl_slIsFeatureLoaded(i32 %a0, ptr %a1) { +entry: + ret i32 -1 +} +define i32 @lsl_slGetFeatureRequirements(i32 %a0, ptr %a1) { +entry: + ret i32 -1 +} +define i32 @lsl_slGetFeatureFunction(i32 %a0, ptr %a1, ptr %a2) { +entry: + ret i32 -1 +} +define i32 @lsl_slGetNewFrameToken(ptr %a0, ptr %a1) { +entry: + ret i32 -1 +} +define i32 @lsl_slSetTagForFrame(ptr %a0, ptr %a1, ptr %a2, i32 %a3, ptr %a4) { +entry: + ret i32 -1 +} +define i32 @lsl_slSetConstants(ptr %a0, ptr %a1, ptr %a2) { +entry: + ret i32 -1 +} +define i32 @lsl_slEvaluateFeature(i32 %a0, ptr %a1, ptr %a2, i32 %a3, ptr %a4) { +entry: + ret i32 -1 +} +define i32 @lsl_slFreeResources(i32 %a0, ptr %a1) { +entry: + ret i32 -1 +} +define i32 @lsl_call_p(ptr %fn, ptr %a0) { +entry: + %r = call i32 %fn(ptr %a0) + ret i32 %r +} +define i32 @lsl_call_pp(ptr %fn, ptr %a0, ptr %a1) { +entry: + %r = call i32 %fn(ptr %a0, ptr %a1) + ret i32 %r +} +define i32 @lsl_call_ppp(ptr %fn, ptr %a0, ptr %a1, ptr %a2) { +entry: + %r = call i32 %fn(ptr %a0, ptr %a1, ptr %a2) + ret i32 %r +} +define i32 @lsl_call_ip(ptr %fn, i32 %a0, ptr %a1) { +entry: + %r = call i32 %fn(i32 %a0, ptr %a1) + ret i32 %r +} diff --git a/runtime/native/vk_win.ll b/runtime/native/vk_win.ll index 61064133..0fa872af 100644 --- a/runtime/native/vk_win.ll +++ b/runtime/native/vk_win.ll @@ -27,6 +27,22 @@ entry: %open = icmp ne ptr %have, null br i1 %open, label %ok, label %load load: + ; Streamline: when the renderer asked for it (lvk_sl_prefer) and sl.interposer.dll is found, the + ; interposer IS the loader - it proxies every Vulkan command, and intercepts the few its DLSS and + ; Reflex plugins need. Without it, vulkan-1.dll as always. + %want = load i32, ptr @lvk_sl_want + %try = icmp ne i32 %want, 0 + br i1 %try, label %sl, label %plain +sl: + %sll = call ptr @LoadLibraryA(ptr @.lsl_dll) + %slmiss = icmp eq ptr %sll, null + br i1 %slmiss, label %plain, label %slok +slok: + store i32 1, ptr @lvk_sl_on + store ptr %sll, ptr @lvk_lib + %sln = call i32 @lvk_bind() + br label %ok +plain: %lib = call ptr @LoadLibraryA(ptr @.lvk_dll) %none = icmp eq ptr %lib, null br i1 %none, label %fail, label %bind @@ -59,3 +75,251 @@ entry: %r = zext i1 %there to i32 ret i32 %r } + +; ---- Streamline (NVIDIA DLSS, Reflex) ----------------------------------------------------------- +; lvk_sl_prefer(1) before lvk_open makes the loader try sl.interposer.dll first; lvk_sl_active says +; whether it did. The sl* thunks resolve their exports from the opened library once and return -1 +; (not sl::Result::eOk) when it has none - vulkan-1.dll, or no library at all. +@.lsl_dll = private unnamed_addr constant [18 x i8] c"sl.interposer.dll\00" +@lvk_sl_want = internal global i32 0 +@lvk_sl_on = internal global i32 0 +define void @lvk_sl_prefer(i32 %on) { +entry: + store i32 %on, ptr @lvk_sl_want + ret void +} +define i32 @lvk_sl_active() { +entry: + %v = load i32, ptr @lvk_sl_on + ret i32 %v +} +@.lsl_n_slInit = private unnamed_addr constant [7 x i8] c"slInit\00" +@lsl_p_slInit = internal global ptr null +define i32 @lsl_slInit(ptr %a0, i64 %a1) { +entry: + %have = load ptr, ptr @lsl_p_slInit + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slInit) + store ptr %found, ptr @lsl_p_slInit + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slInit + %r = call i32 %fp(ptr %a0, i64 %a1) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slShutdown = private unnamed_addr constant [11 x i8] c"slShutdown\00" +@lsl_p_slShutdown = internal global ptr null +define i32 @lsl_slShutdown() { +entry: + %have = load ptr, ptr @lsl_p_slShutdown + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slShutdown) + store ptr %found, ptr @lsl_p_slShutdown + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slShutdown + %r = call i32 %fp() + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slIsFeatureSupported = private unnamed_addr constant [21 x i8] c"slIsFeatureSupported\00" +@lsl_p_slIsFeatureSupported = internal global ptr null +define i32 @lsl_slIsFeatureSupported(i32 %a0, ptr %a1) { +entry: + %have = load ptr, ptr @lsl_p_slIsFeatureSupported + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slIsFeatureSupported) + store ptr %found, ptr @lsl_p_slIsFeatureSupported + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slIsFeatureSupported + %r = call i32 %fp(i32 %a0, ptr %a1) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slIsFeatureLoaded = private unnamed_addr constant [18 x i8] c"slIsFeatureLoaded\00" +@lsl_p_slIsFeatureLoaded = internal global ptr null +define i32 @lsl_slIsFeatureLoaded(i32 %a0, ptr %a1) { +entry: + %have = load ptr, ptr @lsl_p_slIsFeatureLoaded + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slIsFeatureLoaded) + store ptr %found, ptr @lsl_p_slIsFeatureLoaded + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slIsFeatureLoaded + %r = call i32 %fp(i32 %a0, ptr %a1) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slGetFeatureRequirements = private unnamed_addr constant [25 x i8] c"slGetFeatureRequirements\00" +@lsl_p_slGetFeatureRequirements = internal global ptr null +define i32 @lsl_slGetFeatureRequirements(i32 %a0, ptr %a1) { +entry: + %have = load ptr, ptr @lsl_p_slGetFeatureRequirements + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slGetFeatureRequirements) + store ptr %found, ptr @lsl_p_slGetFeatureRequirements + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slGetFeatureRequirements + %r = call i32 %fp(i32 %a0, ptr %a1) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slGetFeatureFunction = private unnamed_addr constant [21 x i8] c"slGetFeatureFunction\00" +@lsl_p_slGetFeatureFunction = internal global ptr null +define i32 @lsl_slGetFeatureFunction(i32 %a0, ptr %a1, ptr %a2) { +entry: + %have = load ptr, ptr @lsl_p_slGetFeatureFunction + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slGetFeatureFunction) + store ptr %found, ptr @lsl_p_slGetFeatureFunction + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slGetFeatureFunction + %r = call i32 %fp(i32 %a0, ptr %a1, ptr %a2) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slGetNewFrameToken = private unnamed_addr constant [19 x i8] c"slGetNewFrameToken\00" +@lsl_p_slGetNewFrameToken = internal global ptr null +define i32 @lsl_slGetNewFrameToken(ptr %a0, ptr %a1) { +entry: + %have = load ptr, ptr @lsl_p_slGetNewFrameToken + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slGetNewFrameToken) + store ptr %found, ptr @lsl_p_slGetNewFrameToken + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slGetNewFrameToken + %r = call i32 %fp(ptr %a0, ptr %a1) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slSetTagForFrame = private unnamed_addr constant [17 x i8] c"slSetTagForFrame\00" +@lsl_p_slSetTagForFrame = internal global ptr null +define i32 @lsl_slSetTagForFrame(ptr %a0, ptr %a1, ptr %a2, i32 %a3, ptr %a4) { +entry: + %have = load ptr, ptr @lsl_p_slSetTagForFrame + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slSetTagForFrame) + store ptr %found, ptr @lsl_p_slSetTagForFrame + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slSetTagForFrame + %r = call i32 %fp(ptr %a0, ptr %a1, ptr %a2, i32 %a3, ptr %a4) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slSetConstants = private unnamed_addr constant [15 x i8] c"slSetConstants\00" +@lsl_p_slSetConstants = internal global ptr null +define i32 @lsl_slSetConstants(ptr %a0, ptr %a1, ptr %a2) { +entry: + %have = load ptr, ptr @lsl_p_slSetConstants + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slSetConstants) + store ptr %found, ptr @lsl_p_slSetConstants + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slSetConstants + %r = call i32 %fp(ptr %a0, ptr %a1, ptr %a2) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slEvaluateFeature = private unnamed_addr constant [18 x i8] c"slEvaluateFeature\00" +@lsl_p_slEvaluateFeature = internal global ptr null +define i32 @lsl_slEvaluateFeature(i32 %a0, ptr %a1, ptr %a2, i32 %a3, ptr %a4) { +entry: + %have = load ptr, ptr @lsl_p_slEvaluateFeature + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slEvaluateFeature) + store ptr %found, ptr @lsl_p_slEvaluateFeature + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slEvaluateFeature + %r = call i32 %fp(i32 %a0, ptr %a1, ptr %a2, i32 %a3, ptr %a4) + ret i32 %r +absent: + ret i32 -1 +} +@.lsl_n_slFreeResources = private unnamed_addr constant [16 x i8] c"slFreeResources\00" +@lsl_p_slFreeResources = internal global ptr null +define i32 @lsl_slFreeResources(i32 %a0, ptr %a1) { +entry: + %have = load ptr, ptr @lsl_p_slFreeResources + %known = icmp ne ptr %have, null + br i1 %known, label %call, label %look +look: + %found = call ptr @lvk_sym(ptr @.lsl_n_slFreeResources) + store ptr %found, ptr @lsl_p_slFreeResources + %none = icmp eq ptr %found, null + br i1 %none, label %absent, label %call +call: + %fp = load ptr, ptr @lsl_p_slFreeResources + %r = call i32 %fp(i32 %a0, ptr %a1) + ret i32 %r +absent: + ret i32 -1 +} +; a feature's own functions (slDLSSSetOptions, slReflexSleep ...) come from slGetFeatureFunction +define i32 @lsl_call_p(ptr %fn, ptr %a0) { +entry: + %r = call i32 %fn(ptr %a0) + ret i32 %r +} +define i32 @lsl_call_pp(ptr %fn, ptr %a0, ptr %a1) { +entry: + %r = call i32 %fn(ptr %a0, ptr %a1) + ret i32 %r +} +define i32 @lsl_call_ppp(ptr %fn, ptr %a0, ptr %a1, ptr %a2) { +entry: + %r = call i32 %fn(ptr %a0, ptr %a1, ptr %a2) + ret i32 %r +} +define i32 @lsl_call_ip(ptr %fn, i32 %a0, ptr %a1) { +entry: + %r = call i32 %fn(i32 %a0, ptr %a1) + ret i32 %r +}