javakotlinjnaopenvr

Jna, Pointer already mapped to Proxy interface


I am trying to do a kotlin port of the openvr java binding and also updating it to 1.0.3

I got at the point of writing the IVRSystem struct/class

I wrote all the methods manually to be sure that won't be any error from the automatic translator in Intellij

I got rid of all the errors coming from a different number of fields from getFieldOrder() but now I still get an error:

Exception in thread "main" java.lang.IllegalStateException: Pointer native@0xffffffff already mapped to Proxy interface to native function@0xffffffff (IVRSystem$GetEyeToHeadTransform_callback).
Native code may be re-using a default function pointer, in which case you may need to use a common Callback class wherever the function pointer is reused.
    at com.sun.jna.CallbackReference.getCallback(CallbackReference.java:124)
    at com.sun.jna.CallbackReference.getCallback(CallbackReference.java:107)
    at com.sun.jna.Pointer.getValue(Pointer.java:430)
    at com.sun.jna.Structure.readField(Structure.java:705)
    at com.sun.jna.Structure.read(Structure.java:565)
    at IVRSystem.<init>(vr.kt:2091)
    at VrKt.VR_Init(vr.kt:2116)
    at VrKt.main(vr.kt:2133)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

According to this comment, it looks like there are multiple calls to a specific callback (GetEyeToHeadTransform_callback?), but it is not, I checked and double checked the code, there is one and only one reference to that callback..

What else might it be?

Edit:

first, this happens when I read() on IVRSysten class, but I can't avoid that...

second, I see that here all the previous methods get real addresses, such as native@0x7fee4bebfd0, only GetEyeToHeadTransform gets always native@0xffffffff...

Edit2:

investigating the original code

dprintf("GetRecommendedRenderTargetSize %p\n", &vr::IVRSystem::GetRecommendedRenderTargetSize);
dprintf("GetProjectionMatrix %p\n", &vr::IVRSystem::GetProjectionMatrix);
dprintf("GetProjectionRaw %p\n", &vr::IVRSystem::GetProjectionRaw);
dprintf("ComputeDistortion %p\n", &vr::IVRSystem::ComputeDistortion);
dprintf("GetEyeToHeadTransform %p\n", &vr::IVRSystem::GetEyeToHeadTransform);
dprintf("GetTimeSinceLastVsync %p\n", &vr::IVRSystem::GetTimeSinceLastVsync);
dprintf("GetD3D9AdapterIndex %p\n", &vr::IVRSystem::GetD3D9AdapterIndex);
dprintf("GetDXGIOutputInfo %p\n", &vr::IVRSystem::GetDXGIOutputInfo);
dprintf("IsDisplayOnDesktop %p\n", &vr::IVRSystem::IsDisplayOnDesktop);
dprintf("SetDisplayVisibility %p\n", &vr::IVRSystem::SetDisplayVisibility);
dprintf("GetDeviceToAbsoluteTrackingPose %p\n", &vr::IVRSystem::GetDeviceToAbsoluteTrackingPose);
dprintf("ResetSeatedZeroPose %p\n", &vr::IVRSystem::ResetSeatedZeroPose);
dprintf("GetSeatedZeroPoseToStandingAbsoluteTrackingPose %p\n", &vr::IVRSystem::GetSeatedZeroPoseToStandingAbsoluteTrackingPose);

prints out

GetRecommendedRenderTargetSize 0109871D
GetProjectionMatrix 0109AACC
GetProjectionRaw 0109AAD1
ComputeDistortion 0109AAF9
GetEyeToHeadTransform 0109AAC2
GetTimeSinceLastVsync 0109AAE5
GetD3D9AdapterIndex 0109AAF4
GetDXGIOutputInfo 0109AADB
IsDisplayOnDesktop 0109AAEA
SetDisplayVisibility 0109AAE0
GetDeviceToAbsoluteTrackingPose 0109AAEF
ResetSeatedZeroPose 0109AAD6
GetSeatedZeroPoseToStandingAbsoluteTrackingPose 0109AAC7

GetEyeToHeadTransform and GetSeatedZeroPoseToStandingAbsoluteTrackingPose have different pointers..


Solution

  • The native code is using a "magic" value of -1 for more than one callback signature. When this callback code for JNA was written, it was assumed that having the same function pointer be mapped to two different signatures should be an error.

    I'm guessing that -1 value means something like "use the default callback" (when arguably simply a NULL pointer might have sufficed, unless the library is using NULL to indicate not to call the callback).

    You might get around this by overriding Structure.read() or Structure.readField() to return a magic value or constant callback object when you see the -1 value, e.g.

    public void read() {
        Memory old = getPointer();
        Memory m = autoAllocate(size());
        // horribly inefficient, but it'll do
        m.write(0, old.getByteArray(0, size()), 0, size());
        useMemory(m);
        // Zero out the problematic callbacks
        for (field : problematic_fields) {
            m.setPointer(field_offset, null);
        }
        super.read();
        useMemory(old);
    }