I’m a bit out of the loop. Has anyone solved it?
(or at least Wayland+Vulkan without libwayland-client)
Sorry, the voting mechanism in the other thread I opened was driving me nuts.
I’m a bit out of the loop. Has anyone solved it?
(or at least Wayland+Vulkan without libwayland-client)
Sorry, the voting mechanism in the other thread I opened was driving me nuts.
There is basically nothing actually ‘done’ here and this is more so my forever project that I come back to. But I am attempting to make but a wayland client and wayland compositor from as much zig as possible and just as a continuous challenge to myself.
https://codeberg.org/0mn1a/Juniper/src/branch/main/src/protocol/WaylandProtocol.zig
So far I can capture the registry global events, you can see it being used here:
https://codeberg.org/0mn1a/Juniper/src/branch/main/src/frontend/main.zig
The design comes down to modeling both sides of the wayland protocol via the same interface, just switched on compile time flag for client or server. I’m trying to do this in the most performant way possible, and I actively rewrite things the moment I learn how to do it a better way. Eventually I envision it becoming a ‘session’ based wayland compositor. So kind of like think if tmux was for all of your applications, where you switch on ‘work’ and ‘gaming’ and they’re entirely different sessions with their own set of workspaces. As well as just going full vim on the controls, as well as full mouse based navigation where a flower wheel appears around the cursor when you hit left and right click.
A bonus link to my compile time Struct of Aligned Arrays with a monomorphised (? I’m not sure that is entirely the most appropriate word) query that generates out the most optimal simd operation I could come up with to query on a set of fields of the Struct.
https://codeberg.org/0mn1a/Juniper/src/branch/main/src/utility/AlignedStructOfArrays.zig
Any who, if I hit the lotto I’d probably just work on this for a couple of years or something.
I’m really interested to see what other people have done.
In Zig, not much.
Thomas Leonard traced through a bunch of this stuff in OCaml to disintermediate GLFW and Wayland for Vulkan:
$ readelf -d /nix/store/bkrss6dcqrxq8ndvjia6vkl004k9wkyi-vulkan-loader-1.4.328.0/lib/libvulkan.so.1.4.328
Dynamic section at offset 0x8c318 contains 31 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libvulkan.so.1]
0x000000000000001d (RUNPATH) Library runpath: [/nix/store/pdwlyjkmc6icc0wb5gaw08m9ynqrg683-glibc-2.40-218/lib]
0x000000000000000c (INIT) 0x9000
0x000000000000000d (FINI) 0x59d98
0x0000000000000019 (INIT_ARRAY) 0x8cec0
0x000000000000001b (INIT_ARRAYSZ) 16 (bytes)
0x000000000000001a (FINI_ARRAY) 0x8ced0
0x000000000000001c (FINI_ARRAYSZ) 16 (bytes)
0x0000000000000004 (HASH) 0x270
0x000000006ffffef5 (GNU_HASH) 0xbe0
0x0000000000000005 (STRTAB) 0x34f0
0x0000000000000006 (SYMTAB) 0x1540
0x000000000000000a (STRSZ) 7675 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x8d548
0x0000000000000002 (PLTRELSZ) 1512 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x7c10
0x0000000000000007 (RELA) 0x5630
0x0000000000000008 (RELASZ) 9696 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW
0x000000006ffffffe (VERNEED) 0x5590
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x52ec
0x000000006ffffff9 (RELACOUNT) 129
0x0000000000000000 (NULL) 0x0
Ok, I’ll implement DT_RUNPATH handling later today (currently on vacation with family). Maybe I’ll install nix in a VM to validate that everything works too.
Why do you think that will help? /nix/store/pdwlyjkmc6icc0wb5gaw08m9ynqrg683-glibc-2.40-218/lib only has glibc stuff in it, so I don’t see how this would lead to finding the other libs.
From the logs you gave earlier:
error(dynamic_library_loader): cannot find library libz.so.1
error(dynamic_library_loader): unresolved libvulkan_freedreno.so dependency: libz.so.1
error(dynamic_library_loader): dlopen("/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/libvulkan_freedreno.so", 0x1) failed: error.LibraryNotFound
My interpretation is:
dlopen libvulkan_freedreno.so/nix/store/i7my973ka8kddq8rg62znr6ckz1xa7wl-mesa-25.2.6/lib/ because either dlopen is called with an absolute path or it is one of the LD_LIBRARY_PATH entries you passed in the nix shelllibvulkan_freedreno.so has libz.so.1 in its DT_NEEDED entrieslibz.so.1 is not in LD_LIBRARY_PATH nor in the hardcoded standard linux lib dirs, so it failsI assume the DT_RUNPATH entry of libvulkan_freedreno.so contains the directory where libz.so.1 is located.
But again, I’m not a nix user and maybe I’m missing something obvious.
Last I looked into this, the Vulkan Windowing System Interface for Wayland requires an opaque pointer to a libwayland-client surface:
// Provided by VK_KHR_wayland_surface
typedef struct VkWaylandSurfaceCreateInfoKHR {
VkStructureType sType;
const void* pNext;
VkWaylandSurfaceCreateFlagsKHR flags;
struct wl_display* display;
struct wl_surface* surface;
} VkWaylandSurfaceCreateInfoKHR;
VkResult vkCreateWaylandSurfaceKHR(
VkInstance instance,
const VkWaylandSurfaceCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSurfaceKHR* pSurface);
Although the mesa implementation uses linux-dmabuf internally, it gets the drm file descriptors from the swapchain using private apis. My feeling is that since the Wayland WSI extension uses the libwayland-client abstractions directly for communicating with the compositor, the Vulkan interface makes it really hard to avoid using libwayland-client.
Also curious if someone has found a way around this! (I guess you’d need to roll your own swapchain?)
I hadn’t noticed this before, but I now see:
// Provided by VK_EXT_acquire_drm_display
VkResult vkAcquireDrmDisplayEXT(
VkPhysicalDevice physicalDevice,
int32_t drmFd,
VkDisplayKHR display);
Which may make it possible to get a VkSurfaceKHR for use with a swapchain and still interop with the compositor directly using linux-dmabuf, but I am not sure and hope someone can chime in with more info.
@andrewrk in your Nix expression it could be that by referencing the packages directly (pkgs.whatever) you’re only including the default outputs of the packages (out, bin, etc…it depends). The lib output is usually separate, so you’d need pkgs.whatever.lib.
Just a thought, haven’t looked any deeper since I’m not at a computer.
I reproduced the issue you faced in a NixOS virtual machine. For the libvulkan_freedreno.so failure example I used to illustrate my understanding of the problem (maybe I wasn’t clear that it was just an example, you posted the readelf output for libvulkan.so.1.4.328 but this library was not part of the failure root cause I think, problem was that none of the vulkan ICDs .so files could load because of the inability for the dynamic loader to resolve dependencies), I get:
$ readelf -d /nix/store/gqpsfqzgbcbwbh12n3sjxk2x4gqgy8lq-mesa-26.1.3/lib/libvulkan_freedreno.so
Dynamic section at offset 0xefbab8 contains 49 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libzstd.so.1]
0x0000000000000001 (NEEDED) Shared library: [libdrm.so.2]
0x0000000000000001 (NEEDED) Shared library: [libxcb-dri3.so.0]
0x0000000000000001 (NEEDED) Shared library: [libwayland-client.so.0]
0x0000000000000001 (NEEDED) Shared library: [libxcb.so.1]
0x0000000000000001 (NEEDED) Shared library: [libX11-xcb.so.1]
0x0000000000000001 (NEEDED) Shared library: [libxcb-present.so.0]
0x0000000000000001 (NEEDED) Shared library: [libxcb-xfixes.so.0]
0x0000000000000001 (NEEDED) Shared library: [libxcb-sync.so.1]
0x0000000000000001 (NEEDED) Shared library: [libxcb-randr.so.0]
0x0000000000000001 (NEEDED) Shared library: [libxcb-shm.so.0]
0x0000000000000001 (NEEDED) Shared library: [libxshmfence.so.1]
0x0000000000000001 (NEEDED) Shared library: [libxcb-keysyms.so.1]
0x0000000000000001 (NEEDED) Shared library: [libdisplay-info.so.3]
0x0000000000000001 (NEEDED) Shared library: [libudev.so.1]
0x0000000000000001 (NEEDED) Shared library: [libexpat.so.1]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libvulkan_freedreno.so]
0x0000000000000010 (SYMBOLIC) 0x0
0x000000000000001d (RUNPATH) Library runpath: [/nix/store/lawdl6fwyj55dign3dasn96cy1asgags-expat-2.8.1/lib:/nix/store/q02dhp7v94jd68hd6llwxfk2i7acbgm6-libdisplay-info-0.3.0/lib:/nix/store/3rjzxgsnsb6qq6xazc8lc3m39vkf0jra-libdrm-2.4.133/lib:/nix/store/flhvh2a7c578zbca9arvn4n2vc2pcgpc-libx11-1.8.13/lib:/nix/store/xpzd2xmvqpkv1la78qkfpybkfwak27xd-libxcb-1.17.0/lib:/nix/store/9wc63l41ivbx6wqwhpijv6vcpjp0p9fc-libxshmfence-1.3.3/lib:/nix/store/bv90l7ic5p0alcjqlcwmgxf76zx3zg20-systemd-minimal-libs-260.2/lib:/nix/store/clvzgv5kqq953s4azqhkg3ddgpvgl9wc-wayland-1.25.0/lib:/nix/store/lap23knfaqyz066bln9pzbrk0vcz11hz-libxcb-keysyms-0.4.1/lib:/nix/store/fsvb5zrsm1n7m5wshm570imspffi7i8f-zstd-1.5.7/lib:/nix/store/8kvxvr3pmsypxiypq4g8zy13glnfr7nx-glibc-2.42-67/lib:/nix/store/hngmi01i8wgi25a0byrxcn4ysz5j79mw-gcc-15.2.0-lib/lib]
0x000000000000000c (INIT) 0xaa000
0x000000000000000d (FINI) 0x559f34
0x0000000000000019 (INIT_ARRAY) 0xe754b0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0xe754b8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x0000000000000004 (HASH) 0x308
0x000000006ffffef5 (GNU_HASH) 0x47c0
0x0000000000000005 (STRTAB) 0x12380
0x0000000000000006 (SYMTAB) 0x47f0
0x000000000000000a (STRSZ) 194262 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0xefbe08
0x0000000000000002 (PLTRELSZ) 11808 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0xa6c28
0x0000000000000007 (RELA) 0x42f38
0x0000000000000008 (RELASZ) 408816 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) SYMBOLIC BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW
0x000000006ffffffe (VERNEED) 0x42ca8
0x000000006fffffff (VERNEEDNUM) 5
0x000000006ffffff0 (VERSYM) 0x41a56
0x000000006ffffff9 (RELACOUNT) 15176
0x0000000000000000 (NULL) 0x0
I’m pretty confident at this point that DT_RUNPATH is used as a LD_LIBRARY_PATH substitute when using nix (not a bad idea really). I will stick with my idea of handling DT_RUNPATH and see where I end up.
Partially correct. Nix embeds the store paths of direct dependencies into DT_RUNPATH so that they’re located reproducibly instead of found at global, mutable locations like /usr/lib/whatever. If I remember correctly, the loader consults DT_RUNPATH after LD_LIBRARY_PATH so that you can still override the library search path at runtime for a Nix-built binary.
I see, it makes sense. LD_LIBRARY_PATH is always searched first. I will implement DT_RUNPATH handling accordingly. And I saw on wikipedia that like LD_LIBRARY_PATH, DT_RUNPATH can contain $ORIGIN/$PLATFORM/$LIB. I’ll support that as well just in case.
EDIT:
From man dlopen:
...
• (ELF only) If the calling object contains a DT_RUNPATH tag, then the directories listed in that tag are searched.
...
… I remember why I wasn’t in the mood to implement RUNPATH… and it seems way worse for RPATH actually ![]()
Made some progress ![]()
I need to clean up a little bit and check that I didn’t break other systems though.
The shell.nix I ended up with is pretty simple (by the way, what is this syntax??? I’m not even sure I really understand what is happening here):
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = with pkgs; [
vulkan-loader
vulkan-validation-layers
libx11
];
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.vulkan-loader
pkgs.libx11
];
}
I didn’t implement the dlopen thing yet. It seems it is not really necessary here. And RPATH handling is not implemented at all.
I tested in a fresh NixOS Gnome / Wayland installation (in VirtualBox). I’m surprised that Vulkan is not directly available to userspace apps though. Is it normal when using nix? It bothers me because the whole point of distributing static apps is to not require anything from end users…
EDIT:
Code has been pushed. I think I’ll make a wayland-vulkan-triangle later this week.
Next goal is to be able to launch the executable on NixOS Gnome without a custom shell.nix.
The static way ![]()
Awesome!!
I know, it’s the worst ![]()
That shell.nix looks great though. Basically the system is about “purity”; you only have installed in each shell what you want the application to see. It’s quite handy for project maintenance because you can avoid a situation where you have something installed without realizing it leading to sneaky dependencies making it tricky for contributors to build from source. So it’s totally normal and reasonable for a nixos user that they would need to have this shell ready to go for such applications. The fact that you don’t have to run patchelf to fix the dynamic loader or add rpaths is already fantastic.
You are going to have to use mkDerivation and wrap it with with the appropriate vars most likely:
https://nixos.wiki/wiki/Nix_Cookbook?__cf_chl_f_tk=OAPe3TGQs3SQLQPvU3jqoeqUIW5tJOCr5oVMpuIpE1w-1782866620-1.0.1.1-fuWB7gUx5C4NjEQvw0zU.VZCsWWUDqAz0AqfIErrmvk
With bdec9f13470d6f9cc0bdf84af8e240d9949c6a6c I got this output, but it worked on my computer too!
[nix-shell:~/src/dyn-loader]$ ./zig-out/bin/x11_vulkan_triangle
info: loading 'libX11.so.6'...
info: loading 'libvulkan.so.1'...
debug: getting vulkan symbol vkCreateInstance
debug: vulkan symbol vkCreateInstance: got address 0x7549c3edebf0
debug: getting vulkan symbol vkGetInstanceProcAddr
debug: vulkan symbol vkGetInstanceProcAddr: got address 0x7549c3ede0b0
debug: getting vulkan symbol vkEnumerateInstanceVersion
debug: vulkan symbol vkEnumerateInstanceVersion: got address 0x7549c3ede820
debug: getting vulkan symbol vkEnumerateInstanceLayerProperties
debug: vulkan symbol vkEnumerateInstanceLayerProperties: got address 0x7549c3ede550
debug: getting vulkan symbol vkEnumerateInstanceExtensionProperties
debug: vulkan symbol vkEnumerateInstanceExtensionProperties: got address 0x7549c3ede270
error(dynamic_library_loader): cannot find library libd3d12.so
error(dynamic_library_loader): dlopen("libd3d12.so", 0x1) failed: error.LibraryNotFound
info: Using device: AMD Radeon RX 7900 XTX (RADV NAVI31)
info: exiting...
Breaking my lurk as I literally just finished rolling my own vulkan-wayland presentation over the past couple of months. Hopefully this info dump is helpful.
I also find this interesting to the main thread, as presentation is the primary overlap between vulkan and wayland. Being able to do presentation without libc essentially cuts wayland out of the picture entirely for this discussion, since, as has already been established, there is nothing on the solely-wayland side of the venn diagram that requires libc.
Presentation can be split into two pieces: images/buffers, and synchronization.
VK_KHR_wayland_surfaceThis requires VK_KHR_external_memory_fd, VK_EXT_external_memory_dma_buf, and VK_EXT_image_drm_format_modifier, which are all standard in vulkan 1.2, as well as the Linux DMA-BUF wayland extension.
The basic idea is to figure out a valid format+modifier to use for the image memory, let vulkan allocate and export a handle to it, and then give that handle to the wayland compositor.
zwp_linux_dmabuf_feedback_v1vkGetPhysicalDeviceImageFormatProperties2 chained with vkPhysicalDeviceExternalImageFormatInfo and vkPhysicalDeviceImageDrmFormatModifierInfoEXT with tiling set to DRM_FORMAT_MODIFIER_EXTvkCreateImage with tiling set to DRM_FORMAT_MODIFIER_EXT chained with vkExternalMemoryImageCreateInfo with handle types set to DMA_BUF_BIT_EXT chained with vkImageDrmFormatModifierListCreateInfoEXT (alternatively, skip step 2 and let vulkan pick the format+modifier by passing all potential format+modifiers to this call and then querying which was chosen)vkAllocateMemory chained with vkExportMemoryAllocateInfo with handle types set to DMA_BUF_BIT_EXTvkgetImageSubresourceLayout with aspect mask set to MEMORY_PLANE_0_BIT_EXTvkGetMemoryFdKHR with handle type set to DMA_BUF_BIT_EXTzwp_linux_buffer_params_v1, where stride is the row pitch and plane index is zeroI believe it is also possible to allocate the image using DMA directly and then import it into vulkan.
VK_KHR_swapchainThis requires VK_KHR_external_semaphore_fd which is standard in vulkan 1.1, as well as the DRM synchronization object wayland extension.
The basic idea of presentation is to synchronize access to each swap image between the client CPU, client GPU, and the compositor. The synchronization primitive that wayland supports (and modern vulkan prefers) to accomplish this is the timeline semaphore, specifically the DRM syncobj. Note: in terms of vulkan, timeline semaphores strictly supercede (binary) semaphores and fences. They can be used for any synchronization between host and device (host-host, host-device, device-host, device-device)
vkCreateSemaphore chained with vkExportSemaphoreCreateInfo with handle types set to OPAQUE_FD_BIT * This is a technically incorrect, note belowvkGetSemaphoreFdKHRwp_linux_drm_syncobj_manager_v1::import_timelineAt this point, we can manipulate the semaphores on the host with vkWaitSemaphores, vkSignalSemaphore, and vkQueueSubmit.
However, for more control, we can get the underlying DRM syncobj:
vkPhysicalDevice using vkGetPhysicalDeviceProperties2 chained with vkPhysicalDeviceDrmPropertiesEXT/dev/dri/renderD<minor>DRM_IOCTL_SYNCOBJ_FD_TO_HANDLEWe can now sidestep vulkan and use DRM_IOCTL_SYNCOBJ_WAIT and DRM_IOCTL_SYNCOBJ_SIGNAL directly. Another option is to use DRM_IOCTL_SYNCOBJ_EVENTFD to have an event file descriptor signaled when a timeline point has been reached, which can be very efficiently integrated into a poll/epoll loop, for example.
* The fact that VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT yields a valid syncobj is purely coincidental. The bit to use should instead be VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_DRM_SYNCOBJ_BIT_EXT (or something like this) once it has been implemented. See this khronos tracking issue.
Couldn’t help myself, so I’m explaining the Nix expression you posted:
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = with pkgs; [
vulkan-loader
vulkan-validation-layers
libx11
];
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.vulkan-loader
pkgs.libx11
];
}
Explained in increasing levels of detail:
# This is a function
<arg>: <expr>
# The argument is an "attribute set" ("attrset" for short).
# This is Nix-speak for map/dictionary.
{ <omitted> }: <expr>
# You can destructure the keys, with '...' meaning "the rest of them"
{ arg1, arg2, ...}: <expr>
# You have a key called 'pkgs'
{ pkgs, ...}: <expr>
# It has a default value
{ pkgs ? <default value>, ...}: <expr>
# 'import' means to evaluate a Nix file and provide its value.
# The `{}` allow you to pass arguments (e.g. override defaults) to the
# function contained in the file.
{ pkgs ? import <some Nix file> {}, ...}: <expr>
# <nixpkgs> grabs an ambient Nixpkgs revision from NIX_PATH on your system
{ pkgs ? import <nixpkgs> {}, ...}:
# Nixpkgs contains a function called `mkShell`.
# It creates a shell whose environment is configured via Nix.
# It takes another attrset as an argument.
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell <attrset>
# `mkShell` expects certain arguments, and `packages` is one of them.
# `packages` become `buildInputs` in the underlying `mkDerivation`,
# but I only mention that for the people with existing Nix experience.
# Otherwise, think of `packages` as "the packages I want available."
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkshell {
packages = <list of packages>;
<other attributes (keys)>
}
# `with <attr>; <expr>` allows `<expr>` to use names from `<attr>`
# without repeating it each time, otherwise you'd type `pkgs.foo`, `pkgs.bar`
# etc.
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkshell {
packages = with pkgs; [
vulkan-loader
vulkan-validation-layers
libx11
];
<other attributes (keys)>
}
# ANY attributes passed to `mkShell` that aren't in its "expected" set
# become environment variables. That's what you're doing for VK_LAYER_PATH
# and LD_LIBRARY_PATH.
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = with pkgs; [
vulkan-loader
vulkan-validation-layers
libx11
];
VK_LAYER_PATH = <expr>;
LD_LIBRARY_PATH = <expr>;
}
# "${<expr>}" is how you do string interpolation in Nix.
# The `pkgs.lib.makeLibraryPath` function creates a PATH-like variable
# that contains the `lib` outputs of all packages in the list passed
# to it.
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = with pkgs; [
vulkan-loader
vulkan-validation-layers
libx11
];
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.vulkan-loader
pkgs.libx11
];
}
The fact that you’re using makeLibraryPath means that I think I was correct in this message (go me!): What's the state of the art in Zig ecosystem for Wayland+Vulkan without libc? 2 - #9 by zmitchell
If you want to learn more about how Nix devshells work under the hood, I gave a talk at NixCon last year explaining it (and how a tool from my previous employer improves on certain aspects): https://www.youtube.com/watch?v=Dwop0jb_SO4
The reason the file contains a function is that Nix does magic dependency-injection-like stuff when it evaluates it (it looks at the function arguments to figure out what to pass in…as arguments).
Nice ![]()
This is the expected output. The
error(dynamic_library_loader): dlopen("libd3d12.so", 0x1) failed: error.LibraryNotFound
message is normal: the vulkan loader probes files using dlopen without RTLD_NOLOAD, the custom dynamic loader intercepts the call to handle it, and the internal dlopen implementation logs the absence of the .so file as a non fatal error.
Thanks, appreciated.
I found makeLibraryPath in an example somewhere on the web, but I still don’t fully understand why we can call pkgs.lib.makeLibraryPath because here it is documented as lib.strings.makeLibraryPath. It makes me think that I could call makeLibraryPath directly on pkgs, using some magic “inner namespace digging”.