LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Import Vulkan API libraries to LabVIEW

I am trying to put a Vulkan API into LabVIEW using some dlls because it is written in C so I am kind of confident that it is usable in LV. Now I am starting with initialization of a Vulkan instance and already have some issues. The questions are "Is it even fixable?" and "Should I just make an additional C wrapper for LabVIEW integration?"
Problem statement: I already made sure the dll and LV are 64 bit included in the attachment zip file, and use the wizard as follow:
joshdoe420_0-1728099081315.png

After including some paths, most of the functions cannot be imported, one example shown below:

joshdoe420_1-1728100913210.png

Let's take VkDevice and VkQueue for an example. In the vulkan_core.h file, they are defined using a macro:
L26: #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
L103: VK_DEFINE_HANDLE(VkDevice)

L104: VK_DEFINE_HANDLE(VkQueue)
After some research, I still don't know why LabVIEW cannot interpret this yet.
Another import error is shown in the following image:

joshdoe420_2-1728101501684.png

This is related to undefined definitions, but only some of them should be added. I am working on this, but the full list is as follow: CRTDLL;MIDL_PASS;MRTDLL;Q_MOC_RUN;RC_INVOKED;UNDOCKED_WINDOWS_UCRT;VK_ENABLE_BETA_EXTENSIONS;VK_NO_STDDEF_H;VK_NO_STDINT_H;WINAPI_FAMILY;WINAPI_FAMILY_GAMES;WINAPI_FAMILY_PHONE_APP;_BUILD_STD_MODULE;_CA_SHOULD_CHECK_RETURN;_CA_SHOULD_CHECK_RETURN_WER;_CORECRT_BUILD;_CRTBLD;_CRT_DECLARE_NONSTDC_NAMES;_CRT_FUNCTIONS_REQUIRED;_CRT_LEGACY_X86_FLT_EXCEPTIONS;_CRT_MANAGED_HEAP_NO_DEPRECATE;_CRT_MANAGED_HEAP_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;_CRT_NO_VA_START_VALIDATION;_CRT_OBSOLETE_NO_DEPRECATE;_CRT_OBSOLETE_NO_WARNINGS;_CRT_SECURE_DEPRECATE_MEMORY;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE_GLOBALS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS_GLOBALS;_CRT_SECURE_WARNINGS_MEMORY;_CRT_SUPPRESS_RESTRICT;_CRT_USE_BUILTIN_OFFSETOF;_DEBUG;_DLL;_GUARDOVERFLOW_CRT_ALLOCATORS;_KERNEL_MODE;_MSC_EXTENSIONS;_MSC_VER;_MSVC_CONSTEXPR_ATTRIBUTE;_MSVC_LANG;_M_ARM;_M_ARM64;_M_ARM64EC;_M_CEE;_M_CEE_PURE;_M_HYBRID;_M_HYBRID_X86_ARM64;_M_IA64;_M_IX86;_M_X64;_NO_INLINING;_PFT_VER;_PREFAST_;_USE_32BIT_TIME_T;_USE_ATTRIBUTES_FOR_SAL;_VCRT_BUILD;_VCRT_SAT_1;_W64;_WIN32;_WIN64;__ANDROID__;__ARM_32BIT_STATE;__ARM_ARCH;__ILP32__;__LP64__;__STDC_VERSION__;__aarch64__;__assembler;__clang__;__cplusplus;__cplusplus_cli;__ia64;__midl;__powerpc64__;__riscv;__x86_64__;

0 Kudos
Message 1 of 11
(522 Views)

I think a lot of people who are very concerned about the problems with OpenGL have never actually experienced any problems with OpenGL. They then come to Vulkan and try to write their first bit of graphics code and wonder why Vulkan is built the way it is. "I had to write so much code!" they scream, as they slowly realize what building a complete game engine with a vague list of very open-ended goals will actually entail.

0 Kudos
Message 2 of 11
(463 Views)

I don't have any concerns with OpenGL but it's in C++ so I'd rather write a native C++ application if I were to use it..

0 Kudos
Message 3 of 11
(450 Views)

Update: I managed to make half of the functions recognizable by the wizard tool by replacing macros that the wizard cannot interpret. One example is below:

joshdoe420_0-1728180471844.png

Now there are about half of the functions left, all have the macros VKAPI_ATR and VKAPI_CALL

joshdoe420_1-1728182183726.png

these are being declared in vk_platform.h so I changed the header file to vulkan.h instead because it includes both vk_platform.h and vulkan_core.h:

joshdoe420_2-1728182601559.png

and have the following include/definitions:

joshdoe420_3-1728182901395.png

The reason I add _WIN32 and VK_USE_PLATFORM_WIN32_KHR is because they are necessary to define __stdcall:

  • in vulkan.h:
    joshdoe420_4-1728183386327.png
  • in vk_platform.h:
    joshdoe420_5-1728183438892.png

However it seems that _WIN32 and VK_USE_PLATFORM_WIN32_KHR doesn't do anything significant and preprocessor definition is incomplete but I will try adding some more and see how it goes. If anyone have an idea, your help would be greatly appreciated.

Message 4 of 11
(388 Views)

Watching your attempts here I get the impression that you barely know how to use this API in C, let alone in LabVIEW and that you believe the Import Library Wizard magically will create an import LabVIEW Library that lets you use this API joyfully. But the Import Library Wizard can’t do magic, despite its name.

And magic it would need to allow an automated tool to create a fully working VI library to import any but the most basic APIs just from a header file. Vulkan for sure is anything but basic!

The C syntax is minimalistic, just barely enough to tell a compiler how to create code but far from helping that compiler to create correct code. A pointer in C can be a reference to a scalar, a memory buffer or a reference to a memory buffer, or gasp, really just about anything the programmer imagines. The only way to know what it means is from:

- reading the according function documentation 

-in lack thereof or a very bad documentation which is rather the norm than the exception, from experience based on naming conventions in the variable names

- from existing example C code showing how to use the API correctly

- from trying and testing, crashing and refining and trying again over and over

 

None of this is anything an automated tool can do no matter how much Algorithmic Intelligence you throw at it!

 

It means that even if the Import Library Wizard created VIs for any of your functions, someone who knows C programming very well, is intimate with the API in question and can dream its functions in their sleep and also knows LabVIEW well, will have to review and often significantly modify every VI created by the wizard carefully. Otherwise you simply get a VI library that rather sooner than later will crash your computer, eat your hard drive or try to assassin your sanity!

Rolf Kalbermatter
My Blog
Message 5 of 11
(367 Views)

Yeah I don't know how the wizard works under the hood because I only import .NET assembly before all this, way less hassle.

I did some more digging and it seems that writing a wrapper is the only way now.

0 Kudos
Message 6 of 11
(352 Views)

A .Net assembly contains an extensive type library resource that describes every object and its methods and global functions that assembly exports. It describes for each parameter the exact datatype, and if it is an array or string buffer its lifetime and minimum size requirements and together with the managed nature of .Net which defines how, and when a caller or function can and should allocate or deallocate a memory buffer, you can easily call any managed code as long as both caller and callee use the same managed contract. LabVIEW’s native datatype system is in fact managed too but predates .Net more than 10 years and Microsoft for some “strange” reason decided to develop their own managed environment. 😀

When you select a method name in a .Net node, LabVIEW retrieves the type library entry for that method from the assembly and creates often fairly involved code in the background to translate between its own managed memory space and the .Net managed system before the call and back after the method call returns. If the assembly programmer didn’t do any error when creating the type library this works usually perfect. But there is no such thing as a type library in normal C DLLs. The only thing you have is the header file and a hopefully extensive library documentation in the form of more or less comprehensible text document. But that is prosa text and not suited for automatic processing, not even by an Algorithmic Interpreter (AI).

 

C is the exact antithesis to a managed environment. Every library developer is free to use whatever fancy memory management system he likes and even use several different ones for different APIs because C is a language that tries to put as few limitations on both the C compiler builder and programmer as possible. Virtually anything that could be done with a CPU can be done in C! The C syntax consequently has not even as much as compiler hints about the memory management aspects of parameters because there would be several options and many more variations on them that are hard to formalize in a syntax without severely limiting those options (basically what managed is about).

The Import Library Wizard is simply a header file parser that understands the  C syntax well enough to extract the fundamental information about the functions declared in that header. It’s quite a complex parser as the C syntax is fairly complex to decode, but it is not rocket science. I implemented my own in the past for a project. What it can’t do is invent information that is not in the C syntax itself. Text comments it has to ignore just as naming conventions in parameter names, because they are not formalized and seldom consistent, often not even in the same header file.

Rolf Kalbermatter
My Blog
0 Kudos
Message 7 of 11
(315 Views)

@joshdoe420 wrote:

Yeah I don't know how the wizard works under the hood because I only import .NET assembly before all this, way less hassle.

I did some more digging and it seems that writing a wrapper is the only way now.


It seems to basically map the 1st parameter to the top left input of the VI and make a semi educated guess as to what datatype it is. It's not really a wizard, it's a journeyman at best. 🙂

G# - Award winning reference based OOP for LV, for free! - Qestit VIPM GitHub

Qestit Systems
Certified-LabVIEW-Developer
0 Kudos
Message 8 of 11
(290 Views)

@Yamaeda wrote:

@joshdoe420 wrote:

Yeah I don't know how the wizard works under the hood because I only import .NET assembly before all this, way less hassle.

I did some more digging and it seems that writing a wrapper is the only way now.


It seems to basically map the 1st parameter to the top left input of the VI and make a semi educated guess as to what datatype it is. It's not really a wizard, it's a journeyman at best. 🙂


It's a little smarter than that, but:

 

typedef struct VkBuffer_T *VkBuffer;

 

Is a so called incomplete data type declaration. Even a C compiler can't do anything other than treat it as a pointer to anything (basically equal to void*), except when this data type is more completely declared later on, but that is usually only done in the according source code, since the intention is to have any user of the API treat this parameter indeed as an anonymous pointer.

 

In LabVIEW this is simply a pointer sized integer, but that of course does not provide any security for a LabVIEW user to not pass a VkBuffer to a function that wants to see a VkImage! And crash, bumm! 💣

Rolf Kalbermatter
My Blog
Message 9 of 11
(275 Views)

@rolfk wrote:

In LabVIEW this is simply a pointer sized integer, but that of course does not provide any security for a LabVIEW user to not pass a VkBuffer to a function that wants to see a VkImage! And crash, bumm! 💣


Basically the only way to make such an API LabVIEW safe is to do one of these:

 

1) wrap everything into LabVIEW classes. Those VkBuffers and VkImages etc. are in fact objects, no matter if the underlaying programming is in C or C++. So you would have to map them to according LabVIEW classes and keep the pointer sized integer a private data element of the class, never to be exposed in any way to the LabVIEW user. That way you can prevent the user from accidentally or willfully mistreat those pointers. These VIs can't be created by the Import Library Wizard, and writing your own variant of such a wizard that creates LabVIEW classes instead would be a major project in its own. Some of the functions the API exposes likely will use complex datatypes as parameter that are difficult to interface from LabVIEW since LabVIEW doesn't really know pointers on the diagram level. Traversing arrays of structures containing pointers in pure LabVIEW code, while not impossible is a true exercise in pointer voodoo far beyond what you would need to do when accessing that same structure in C. So you either end up writing a wrapper function in C anyways or start to play advanced C compiler on the LabVIEW diagram of those VIs.

 

2) LabVIEW knows a concept of user refnums, but it is not publicly documented. It requires creating a *.rc and according *.rch file that is placed in the <LabVIEW>\resource\objmgr directory and then a restart of LabVIEW. If the *.rc file does not contain any syntax errors, LabVIEW then knows one or more new refnum datatypes that can wrap a pointer sized integer and is type safe as far as LabVIEW is concerned. There are non-public LabVIEW nodes that can wrap the pointer into such a refnum and also deallocate it at the end just like other refnums. The entire thing is highly complex and not for the faint hearted, any errors anywhere in this can lead to very unexpected effects. So I can't recommend its use to anyone.

 

3) Write a wrapper library in C that exports more LabVIEW friendly APIs. Again you still would have to somehow protect the various object pointers in a way that a casual LabVIEW user is not easily able to mess up. This could be one of the above two options but considering the difficulties with 2) would for anybody but the most diehard nutcases mean to use LabVIEW classes again. 😁

 

And forget about the Import Library Wizard! It's a nice tool if your API is very simple but simply can't properly import complex APIs. If the API is simple, the effort to create the LabVIEW VIs to call the API functions through Call Library Nodes yourself is negligible anyways. If it is complex, you have to go through each and every function anyways, and carefully review and modify it to be correct and LabVIEW user friendly. This usually amounts to at least as much work as if you had created it all yourself from scratch anyways.

 

Basically, if you can't create the VIs yourself from scratch, you won't be able to review the wizard created VIs to be correct and use of such VIs in any application simply would be a major liability.

 

Rolf Kalbermatter
My Blog
Message 10 of 11
(245 Views)