Vulkan 1.0 Release

Hmm… this is a hard problem.
Let’s stay with the facts:

  • we need different functions for different contexts (instance, device)

Let’s assume the following desired properties:

  • we want for LWJGL users to call Vulkan functions easily with methods named after the Vulkan functions

How to provide context?

  1. Like you said, with dispatching via object-orientation using implied this parameters for a given Vulkan instance and/or device (this would mean that it is not possible to call Vulkan methods anymore with static methods)
  2. With adding an additional parameter to each (instance, [device])-sensitive method that takes the manually lookedup Vulkan function pointer (this can also be the low level on which the previously mentioned object-oriented layer can sit on top of)
  3. With letting LWJGL’s Vulkan methods keep track of the respective function pointer given the (instance, [device]) context as method parameters for each method invocation. This means, LWJGL would need to hold state/keep track of the current context (which function was invoked to create an instance/device, etc) in a thread-local manner and for each method invocation choose the correct Vulkan function pointer. Probably not a good idea.

Currently, I find none of the above three methods appealing at all. :slight_smile:
If I had to choose, I would probably opt for 2. and if so definitely head to 1. as soon as possible with 2. providing the basis.

For all SWT fans, I added Vulkan support on Win32 for a SWT Canvas (much like SWT’s GLCanvas for OpenGL support) to the lwjgl3-swt project.

Example code:


Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
VKData data = new VKData(); // <- like GLData for OpenGL
data.instance = instance; // <- The VkInstance created outside with LWJGL 3
VKCanvas canvas = new VKCanvas(shell, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE, data);
long surface = canvas.surface; // <- the VkSurfaceKHR to stuff into VkSwapchainCreateInfoKHR
shell.open();

This allows to embed Vulkan into your SWT/Eclipse application.

The latest nightly build (3.0.0 #28) replaces memFree(StructBuffer) with StructBuffer.free() and adds “auto-size” support for struct members. Both are breaking changes.

[quote=“KaiHH,post:61,topic:56271”]
Lets explore some options with actual code:

// Global commands looked up with vkGetInstanceProcAddr(NULL, ...):
// vkEnumerateInstanceLayerProperties
// vkEnumerateInstanceExtensionProperties
// vkCreateInstance

// ..blahblah
VkInstanceCreateInfo ici = ...;
vkCreateInstance(ici, null, myInstance);

// Option 1: Create binding with explicit instance/device argument, use non-static methods
// Function pointer retrieved from "this".
VKCapabilities caps = new VKCapabilities(myInstance);
caps.VK10.MapMemory(...);
caps.EXTDebugReport.CreateDebugReportCallbackEXT(...);

// and/or
VK10 vk = caps.VK10; // or new VK10(myInstance)
vk.MapMemory(...);
EXTDebugReport debug = caps.EXTDebugReport; // or new EXTDebugReport(myInstance)
debug.CreateDebugReportCallbackEXT(...);

// Option 2: Create binding with explicit instance/device argument, use static methods
// Function pointer retrieved from an extra parameter.
VKCapabilities caps = new VKCapabilities(myInstance);
vkMapMemory(caps, ...);
CreateDebugReportCallbackEXT(caps, ...);

// or
VK10 vk = caps.VK10; // or new VK10(myInstance)
vkMapMemory(vk, ...);
EXTDebugReport debug = caps.EXTDebugReport; // or new EXTDebugReport(myInstance)
CreateDebugReportCallbackEXT(debug, ...);

// Option 3: LWJGL-style, assuming 99% of users will use different threads for different VkInstances/VkDevices.
// Function pointer retrieved from thread-local state, explicitly set by the user.
VKCapabilities caps = new VKCapabilities(myInstance);
VK.setCapabilities(caps); // in each thread

vkMapMemory(...);
CreateDebugReportCallbackEXT(...);

I’m between 2 and 3. I just don’t know how well the assumption in 3 holds.

Okay, I think I changed my mind. You are right, option 3 is LWJGL-style and now I am sooo absolutely in favor of option 3 with the instance hidden in thread-local context!
I think it looks really ugly and is error-prone as hell having to drag the VkInstance (in whatever form) with the method invocations.
But I also had something else in mind, with not storing the function pointers in thread-local, but looking it up based on the actual argument of the Vulkan method invocations, so that even if a user forgot to set the thread-local or used the wrong one, it would still work correctly. But LWJGL would have to do some lookup for each method invocation.

[quote=“KaiHH,post:64,topic:56271”]
Yes, the lookup would be expensive. Another option would be to convert the 5 dispatchable handles from longs to objects:

// ..blahblah
VkInstanceCreateInfo ici = ...;
PointerBuffer p = ...;

vkCreateInstance(ici, null, p);
VkInstance myInstance = new VkInstance(p.get(0)); // creates a VKCapabilities instance internally (using vkGetInstanceProcAddr)

vkEnumeratePhysicalDevices(myInstance, deviceCount, physicalDevices); // function pointer retrieved from myInstance.caps.VK10.EnumeratePhysicalDevices
VkPhysicalDevice pd = new VkPhysicalDevice(myInstance, physicalDevices.get(0)); // pd.caps == myInstance.caps

vkCreateDevice(pd, ..., p); // function pointer retrieved from pd.caps.VK10.CreateDevice
VkDevice dev = new VkDevice(p.get(0)); // dev.caps will be a new VKCapabilities instance created using vkGetDeviceProcAddr

// similarly, VkQueues and VkCommandBuffers will share the VKCapabilities instance of the device from which they are created

The LWJGL generator had support for object handles in the past, it’d be easy to restore that functionality and implement the above. Since there are only 5 dispatchable handles and they correspond to “heavyweight” objects that will not be frequently created/destroyed, I think this a viable solution.

Could you show how actual method invocations of Vulkan functions would look like, then?
Does this now impose an OO design to the solution?
Do methods have to be invoked on the respective VkInstance/VkPhysicalDevice/VkDevice… objects?

Also, one rather peculiar thing I just noticed: Looking up the instance-specific function ‘vkGetPhysicalDeviceSurfaceCapabilitiesKHR’ using VK10.vkGetInstanceProcAddr() with a valid ‘instance’ returns 0L whereas looking it up with getFunctionAddress on the VK10.getLibrary() returns a valid function pointer…

I have to look up whether this is by the spec. This would mean that LWJGL have to try to lookup the instance/device-specific function address, and if that failed, use simply GetProcAddress.

I would have 0 problems replacing all the Vulkan objects stored as long addresses with actual objects. The Vulkan spec does call them objects/structs/whatever after all. There’s no point in returning the long addresses from the functions as well if all functions that used to use them now need the object instead; just create the object for us and return it. And you’re right, Spasi; as long as the objects aren’t frequently created or destroyed we won’t even have a problem with garbage when doing this. My vote is for automatic object wrapping.

[quote=“KaiHH,post:66,topic:56271”]
Exactly like they are now, except that instead of passing (as the first parameter) a long dispatchable handle (they’re always the first parameter), you pass an instance of a Java class (VkInstance, VkDevice, VkQueue, etc). Nothing else changes. There’s no thread-local state and all methods are static.

[quote=“KaiHH,post:66,topic:56271”]
Don’t worry about that, it’s how OpenGL/CL/AL work too. It will be handled automatically on VKCapabilities creation.

[quote=“theagentd,post:67,topic:56271”]
Automatic wrapping is not possible, because all object creations go into output parameters and all Vulkan functions already have a return value (the error code).

As for replacing all handles, it doesn’t make sense for non-dispatchable handles. They won’t hold any other state and the frequency of creation/destruction of such handles would needlessly generate Java garbage. This is also why the OpenCL bindings in LWJGL 3 use primitive handles instead of the object handles we had in LWJGL 2.

[quote=“Spasi,post:68,topic:56271”]

In C you would do something like


VkInstance instance;
vkCreateInstance(..., &instance);

The closest we can get to that is


VkInstance instance = new VkInstance();
vkCreateInstance(..., instance);

which is IMO better than the planned


PointerBuffer instanceBuffer = MemoryUtil.memAllocPointer(1);
vkCreateInstance(..., instanceBuffer);
VkInstance instance = new VkInstance(instanceBuffer.get(0));

And no, don’t make the non-dispatchable handles objects. I just wanted to make a point that whether they are longs or objects make no difference for us as long as the objects aren’t created and destroyed/garbage collected often.

[quote=“theagentd,post:69,topic:56271”]
It’s less typing and better looking, but sacrifices immutability of the handle instance. That’s worse imho. There are also functions that create/return multiple handles and that would require passing (Java) array arguments, which is also something I’d like to avoid.

Okay then. That sounds about perfect from the API side. And no lookups and the best possible performance sounds about the perfect solution! :slight_smile:

You can always mark the object as created after it has been passed into vkCreate***(), so that if it is passed in again it will throw an exception.

Are there any practical problems with passing in an array? I see no immediate problems with doing


VkObject[] objects = new VkObject[numObjects];
vkCreateObject(..., objects);

If I am not mistaken, this all so far has been about VK functions returning handles to dispatchable objects, which (like their names imply) dispatch other function invocations, having them as parameters, to instance/device-specific/sensitive Vulkan functions internally. So we really only need Java objects for those dispatchable objects so that we can efficiently associate with them the needed instance/device-sensitive function pointers.
As far as I can tell, there are no other Vulkan functions that would benefit from having actual Java objects as out parameters. A single LongBuffer will suffice and the single ‘long’ is also what is needed. We don’t need to associate more information with that ‘long’.
And it also seems that those functions creating handles to dispatchable VK objects only return single instances as out parameters and not lists of objects.

[quote=“KaiHH,post:73,topic:56271”]
There is one, vkEnumeratePhysicalDevices.

On arrays: I appreciate your example, it would be nice. But it would be a lot of work to support in the generator, just for a single function (among thousands). Also, it would surprise users, a method that accepts an array instead of a buffer, when no other method does. A lot of effort has gone into LWJGL 3 to keep things consistent across all bindings, the generator is packed with compile and runtime checks because of that.

On handles: I really dislike having uninitialized objects that are later frozen. It works, but it’s bad design. I think the best approach for now is to keep what we have with the PointerBuffer out-params. It’s fine for the same reason having object handles is fine: it’s only 5 handle types that are only going to be allocated a few times, in a few places. I’ll keep thinking about it and if a better solution presents itself, we can always overload the existing methods.

[quote=“Spasi,post:74,topic:56271”]

Ah, missed that one. :slight_smile:
You are right, but does VkPhysicalDevice really dispatch, in a way that is relevant to LWJGL? I mean, is there something like ‘vkGetPhysicalDeviceProcAddr()’ ?
I think I must correct my initial statement: We actually only need object handles for VkInstance and VkDevice, since these are the only types we need to associate function pointers with. (I try to specifically stay with the usecase of dispatching to the correct function pointers here).
Everything else can stay PointerBuffer or LongBuffer.
Or did I miss yet another function? :slight_smile:

There are functions like:

vkGetPhysicalDeviceFeatures(VkPhysicalDevice, ...);
vkQueueSubmit(VkQueue, ...);
vkBeginCommandBuffer(VkCommandBuffer, ...);

These take neither a VkInstance nor a VkDevice. They use the VkPhysicalDevice/VkQueue/VkCommandBuffe handles to dispatch the functions. It’s true that a VkPhysicalDevice will have the same function pointers as the corresponding VkInstance. And VkQueues/VkCommandBuffers will have the same function pointers as the corresponding VkDevice. But we still need to use object handles, else we’d have to do inefficient look-ups.

VkPhysicalDevice, VkQueue and VkCommandBuffer will be very simple classes. E.g. this is what VkQueue looks like atm:

public class VkQueue extends DispatchableHandle {

	public VkQueue(long handle, VkDevice device) {
		super(handle, device.getCapabilities());
	}

}

VkInstance and VkDevice creation on the other hand is much more complex, that’s where the VKCapabilities instances are created using vkGetInstanceProcAddr, vkGetDeviceProcAddr, etc.

Sorry to be the one who asks, but when can we expect this? I’m eager to getting back to work. xd

Nightly build 3.0.0 #29 is up, it has the handle changes and the first attempt at proper function pointer loading. The VKCapabilities class is work in progress, but the rest seem to be working well, including extensions.

Sorry that this isn’t directly related to the conversation, but is Vulkan.

Rumour on the street (see: reddit) is that as Nintendo has recently joined Khronos, is that the new Nintendo console (Nintendo NX) will support Vulkan.

Lmao. Get ready for 15 more mario games.