LWJGL / lwjgl3

LWJGL is a Java library that enables cross-platform access to popular native APIs useful in the development of graphics (OpenGL, Vulkan, bgfx), audio (OpenAL, Opus), parallel computing (OpenCL, CUDA) and XR (OpenVR, LibOVR, OpenXR) applications.
https://www.lwjgl.org
BSD 3-Clause "New" or "Revised" License
4.67k stars 631 forks source link

Clarify documentation of `VKCapabilitiesInstance` and `VKCapabilitiesDevice` #809

Closed ws909 closed 1 year ago

ws909 commented 1 year ago

Description

It is not clear from the description of the VKCapabilitiesInstance and VKCapabilitiesDevice classes if they list available or enabled capabilities.

It is also unclear if instances of these classes always carry the same meaning; available or enabled. If they do not, the documentation for DispatchableHandleInstance::getCapabilities() and DispatchableHandleDevice::getCapabilities() should be changed instead, possibly their subclasses as well, if they also differ.

I was wondering if there was a built-in way of telling which version of Vulkan, and which extensions, were enabled, without keeping track of this myself. Looking at VkPhysicalDevice::getCapabilities(), I figured this might provide an overview of enabled extensions, however, because physical devices are only queried, not created, this seems to not make sense, as no extensions are enabled for them. However, then why does VkDevice have the same method? Surely, these capabilities might tell which extensions and API version are enabled? So do they return the same instance? No. The instances are actually of different classes. So that means that VKCapabilitiesDevice is for enabled capabilities, while VKCapabilitiesInstance is for available capabilities. But then again, VkInstance is created, not queried, and that also returns a VKCapabilitiesInstance instance. So maybe they all state enabled capabilities, then? But if that's case, does LWJGL create different VkPhysicalDevices for each VkDevice. This is very confusing. So apparently, reading into the source code, VKCapabilitiesDevice does list enabled capabilities. And VkPhysicalDevice just saves a reference to the VkInstance's capabilities. And I did not check the implementation for any subclasses of DispatchableHandleDevice other than VkDevice.

Spasi commented 1 year ago

Hey @ws909,

I've added the word "enabled" to the javadoc. Unlike OpenGL, the user is responsible for enabling any Vulkan extension they want to use explicitly. This is by design in Vulkan and LWJGL respects that design. You can discover available extensions with the functions vkEnumerateInstanceExtensionProperties and vkEnumerateDeviceExtensionProperties (for instance and device extensions respectively). See the HelloVulkan demo for sample usage.

Objects like VkPhysicalDevice and VkQueue carry the corresponding capabilities object of their parent, because they are what Vulkan calls "dispatchable handles". Since there is no thread-local state in Vulkan, like the current context in OpenGL, Vulkan functions require another mechanism for dispatching, which is these dispatchable handles, usually the first parameter of a Vulkan function. LWJGL has also wrapped such handles (i.e. opaque pointers) in simple classes and uses their capabilities objects to call the correct function addresses. Also note that, similarly to how different OpenGL contexts may have different addresses for the same OpenGL function, different Vulkan instances/devices may also have different addresses for the same Vulkan function. This is done for efficiency (e.g. calling directly into the device driver, instead of going through an indirection layer first).

ws909 commented 1 year ago

@Spasi

This is a quick fix that very easily explains how one can use these classes, but as they're not part of the Vulkan API, but an extension by LWJGL, I do think they should be documented more. It's very clear already, for anyone working with Vulkan, that these classes have nothing to do with actually enabling any extensions. So the question is just how one can rely on these classes, and if such a reliance is discouraged.

Spasi commented 1 year ago

Hey @ws909,

I'm not sure what you mean exactly. The Vulkan bindings cannot be used without these classes, so users need to rely on them.

Alternatively, users would have to retrieve function pointers (e.g. with vkGetDeviceProcAddr), store them in a custom data structure, then pass them manually to LWJGL on every call. This would make the bindings really awkward to use. LWJGL uses the capabilities classes (since the original OpenGL bindings back in 2002) to solve this problem and make it easier for users to get started.

For the Vulkan API in particular, LWJGL is much less intrusive. For example, to create a VkDevice you don't create and pass a capabilities instance externally, it is done for you internally. You just pass the device handle, physical device handle and VkDeviceCreateInfo, which are all Vulkan concepts. If you check the HelloVulkan sample, there is no reference to the capabilities classes anywhere, it's all standard Vulkan API usage. The VkDevice constructor makes it rather obvious how it should be used.

In any case, PRs are open and you're welcome to contribute any documentation improvements you think might be helpful.

ws909 commented 1 year ago

@Spasi What I mean by relying on them, is if they should be considered internal components of LWJGL. An engine written in C++, for instance, would need to either write these classes itself, or use another way to keep track of which extensions are enabled. Were they written with the purpose to be used by LWJGL clients, or only as a helper for LWJGL itself? It's just unclear what the intentions for these classes are, concerning users of LWJGL.

I would be more than happy to contribute PRs for those issues I am able to resolve. If I would create a PR for the documentation for these classes, it would mostly be a guessing game for me.

SWinxy commented 1 year ago

These look to be generated (by virtue of being in the src/generated path) but are LWJGL-written. It seems like it's just more harnessing to the binding, and there are a couple of other places where there's been additional code inserted. I can improve the documentation myself, and I suggest the issue be reopened.

Spasi commented 1 year ago

@Spasi What I mean by relying on them, is if they should be considered internal components of LWJGL. An engine written in C++, for instance, would need to either write these classes itself, or use another way to keep track of which extensions are enabled. Were they written with the purpose to be used by LWJGL clients, or only as a helper for LWJGL itself? It's just unclear what the intentions for these classes are, concerning users of LWJGL.

Well, both. It's both an essential part of the LWJGL implementation and something useful to the user, something they would have implemented manually otherwise. In any case, it's public API and by default this means it's not an internal component.

More generally, all public classes/methods/fields in LWJGL bindings are part of the official API. Implementation details are hidden away in private & package-private components.

The only public components that are documented to be internal and subject to change, all live under the LWJGL core and the org.lwjgl.system package. Some utilities in there are used internally by bindings and could not be encapsulated properly in a Java 8 code base. In LWJGL 4, any such utilities will probably be hidden away using the functionality provided by Java Modules.