Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bullet C API first thoughts #428

Closed
dustContributor opened this issue Nov 19, 2018 · 67 comments
Closed

Bullet C API first thoughts #428

dustContributor opened this issue Nov 19, 2018 · 67 comments

Comments

@dustContributor
Copy link

dustContributor commented Nov 19, 2018

Hi! This is more of an opinion but now that I've been trying to port my stuff to Bullet's C API and worked a bit with it I'm having doubts about it.

The API itself is very awkward to use. I didn't think it'd be a problem but all the indirect handles and indices you have to keep around to operate on things are very annoying.

The API refuses to work with structs directly, since it's thought as a client-server architecture, what you have on the "client side" are usually int64 handles or int32 indices referring to some item on the server side, and a set of many functions that each mutate/read a field of the logical structure on the server side, one by one.

The API is robotics and Python oriented, so I am guessing it only exposes what is useful and practical for both of them.

This means that something really useful in games like collision filters isn't there by default, you have to enable it through a plugin. Moreover, something like a kinematic body (like say, a player) isn't there at all, so instead of setting the velocity of the player you have to move it around with instantaneous forces (unless there is actually a way and I'm an idiot), using forces this way is very hard to fine tune player movement or anything really.

This also means there is a lot of fluff there that isn't relevant for games at all. There are a lot of core functions there purely dedicated to work with the included visualizer of the physics simulation. Until very recently the only way to load an arbitrary mesh body was through a robotics focused file format, instead of you know, an array of vertices.

What I am trying to say is that the client-server layer between the user and Bullet isn't helping at all. It's just bloat a game doesn't needs.

Now the thing is that for 3D there aren't many physics libraries to use out there. And out of them all, Bullet is probably the most documented and widely used, although that's the case for the plain C++ library, not the C API, I doubt there is any game made with it.

It doesn't means there aren't any alternatives:

Newton Dynamics has a C API, seems pretty clean and it has been used on a few relevant games(Mount & Blade, Frictional Games stuff, ie SOMA and Amnesia). Even less examples although it has a demo project with a bunch of things. It's zlib licenced.

Open Dynamics Engine has been used on a bunch of games (Techland stuff, ie Call of Juarez, Dead Island) and I've read it has a C API... I just don't understand enough of the repo to know where to look for it. Looking for examples I get results from 2006. It's BSD or LGPL licenced. There is a Java port of it but it uses 64 bit floats everywhere and the first issue you see in the repo is an unresolved high garbage creation complaint from 2015, both of them bad signs.

I think I like more Newton Dynamics since it looks more like what you'd expect of a "proper" C library. Like a single header with most of the structs and function definitions for starters. I don't really know how hard it'd be to have bindings for it too so people (like me!) can try it out.

In any case, for now I'll keep trying to work with Bullet's C API and probably nag the author about some missing functionality as/if I find it, since as I've said in another occasion, with it I can get rid of libGDX as a dependency in my project which is a big plus for me.

@ppiastucki
Copy link

ppiastucki commented Nov 19, 2018

Unrelated to LWJGL, but you can try ode4j if you do not mind pure Java solution.

@dustContributor
Copy link
Author

dustContributor commented Nov 19, 2018

There is a Java port of it but it uses 64 bit floats everywhere and the first issue you see in the repo is an unresolved high garbage creation complaint from 2015, both of them bad signs.

I do mind.

@Ali-RS
Copy link

Ali-RS commented Nov 19, 2018

You can also take a look at jMonkeyengine bullet wrapper
https://wiki.jmonkeyengine.org/jme3/advanced/physics.html#technical-overview
https://github.com/jMonkeyEngine/jmonkeyengine

And if you want a sever side bullet physic simulation with an EntityComponentSystem design like what I am doing for my mmorpg, you can consider
https://hub.jmonkeyengine.org/t/ecs-in-complex-3d-games-how-deep-should-it-go/40665/5?u=ali_rs
https://hub.jmonkeyengine.org/t/is-jmonkey-the-right-engine-for-simulating-basic-physics/40714/13?u=ali_rs

it will add bunch of dependencies though ;)

@dustContributor
Copy link
Author

dustContributor commented Nov 19, 2018

it will add bunch of dependencies though ;)

Yeah, trading libGDX for jMonkeyInterop is a side step for me, maybe a step back since jme is even bigger.

In any case, I think this is an issue better suited for a native lib rather than a Java solution, the question is which one.

I didn't know there were other physics engines with a C API, for some reason I thought the other physics engines were C++ only like Bullet was. So I suggested Bullet's C API to Spasi as soon as I found it existed out of ignorance mostly, didn't know about Newton Engine's .

My line of thinking was "Hey, I used Bullet before and it was really straightforward and easy to integrate, its C API cant be that much different!". I was wrong.

@dustContributor
Copy link
Author

Well I cloned Newton's repo and LWJGL's repo since this is a good moment as any to try learn how to make a new binding with it (not that I have the expectation of getting something usable out of my attempt).

From what I've read in https://github.com/LWJGL/lwjgl3/tree/master/doc what I ought to do, since I've followed the setup steps and everything seems to work, is just grab this header file, create a newton.kt and NewtonTypes.kt file, grab a cup of coffee and start typing the data type definitions, and the function definitions?

For the function definitions a chunk of the work could be done with some clever find+replace, but for the data type definitions it seems it's just a lot of typing.

@Spasi
Copy link
Member

Spasi commented Nov 22, 2018

You may want to wait a few days before attempting to create new bindings. I've been working on a tool that extracts LWJGL template definitions from C/C++ headers. I'm currently in the process of trying different headers to fix corner cases and polish it up.

The tool uses libclang (via new Clang/LLVM bindings) to parse the headers and traverse the AST. It detects both declarations and associated comments for documentation. Comments in doxygen format are supported and can be traversed to detect e.g. param/return documentation, but the tool tries to attach any comments it can find.

This is what it currently generates for Newton.h with a single click.

@dustContributor
Copy link
Author

That is awesome, dude! Really, really cool.

Sadly now you solved the part I thought I understood better, and now I have to actually try to compile the thing 😄

@Spasi
Copy link
Member

Spasi commented Nov 26, 2018

On the Bullet bindings: I'm considering disabling Bullet for the 3.2.1 release until there's more feedback.

The bindings will still be available in snapshots after the release. This should give us enough time (until 3.2.2?) to evaluate other options before committing to anything. Maintenance effort should not be wasted on bindings that no one is going to use.

@dustContributor
Copy link
Author

I agree. I do think that LWJGL should offer a physics engine binding, since at least for game development it'd leave LWJGL more rounded. I just don't think Bullet's C API is the one.

Spasi added a commit that referenced this issue Dec 8, 2018
@germangb
Copy link

I've been using Newton lately and the C API is indeed very clean and nice to work with 💯 . It would be a great addition to lwjgl

@dustContributor
Copy link
Author

Any updates on the template generator tool magic thingy?

I gave a quick jab at making that gist working. It's missing a couple things. For example, the definition of the type 'long_long' (which I believe it'd just map like an int64_t), also _Bool,
and after all the types were defined, it was missing the initial section that goes something like:

val Newton = "Nuklear".nativeClass(Module.NEWTON, prefix = "", prefixMethod = "", library = "lwjgl_newton") {
// yadda yadda
}

And I'm getting an NPE while ant generate'ing NewtonUserMeshCollisionRayHitCallback that I don't understand because I have zero clue about Kotlin 😄

Probably there is more stuff I haven't reached yet.

@Spasi
Copy link
Member

Spasi commented Jan 4, 2019

Any updates on the template generator tool magic thingy?

Yes, it's been available for a while. It's fully featured now, but I'm sure there are still corner cases that are not handled properly, so please consider it a beta and report any issues you face.

You'll need to download or build LLVM/Clang on your machine, then run the tool with:

ant extract -Dclang=<path to libclang>

@dustContributor
Copy link
Author

Ah cool, then I already had it and didn't notice it 😄 Thanks!

@dustContributor
Copy link
Author

dustContributor commented Jan 6, 2019

To try out the tool, it seems I have to compile first LLVM bindings. So I try to do that with just enabling LLVM in build-bindings.xml, then doing 'ant compile-templates' but I'm having issues.

First there is this:
https://github.com/LWJGL/lwjgl3/blob/master/modules/lwjgl/llvm/src/templates/kotlin/llvm/templates/ClangIndex.kt#L3059

long_long seems to be missing from the type definitions:
https://github.com/LWJGL/lwjgl3/blob/master/modules/generator/src/main/kotlin/org/lwjgl/generator/GlobalTypes.kt

I add it like:

val long_long = IntegerType("long long", PrimitiveMapping.LONG)

And if I try to compile again, I get an "overload resolution ambiguity" error on those long_long returning functions.

EDIT: Nevermind, I mixed up with the old 'long_long' I had added for Newton, turns out LLVM's bindings had its own long_long defined which clashed with the global one I added.

@dustContributor
Copy link
Author

dustContributor commented Jan 6, 2019

I can launch the tool, but once I add Newton's folders and headers, I get this exception:

java.lang.NoClassDefFoundError: Could not initialize class org.lwjgl.llvm.ClangIndex$Functions
	at org.lwjgl.llvm.ClangIndex.clang_createIndex(ClangIndex.java:2706)
	at org.lwjgl.extract.ExtractionContext.<init>(Extract.kt:60)
	at org.lwjgl.extract.ExtractKt.parse(Extract.kt:174)

It's like ClangIndex is there, but the inner Functions class isn't.

EDIT: Yeah I really don't get it, inspecting the generated ClangIndex.class and ClangIndex$Functions.class, they're there. No idea why the tool can't find it.
EDIT2: Tried re-compiling and and running everything in both Java 8 and Java 11, still having the same issue.
EDIT3: I can't for the life of me make IDEA debug the ant process. I use the debug configuration "GENERATOR" but it wont stop at any breakpoint on the Kotlin code.

@Spasi
Copy link
Member

Spasi commented Jan 6, 2019

This usually means that static initialization of the inner Functions class failed. For example, it can happen if you're using an older libclang version that doesn't expose a newer function pointer. I haven't done any work on versioning the LLVM/Clang bindings, it's likely that the latest version is required.

Try running the tool with:

ant extract -Dclang=<path to libclang> -Djvmargs="-Dorg.lwjgl.util.Debug=true -Dorg.lwjgl.util.DebugLoader=true"

Is there any useful output?

I use the debug configuration "GENERATOR" but it wont stop at any breakpoint on the Kotlin code.

Probably because this launches the Generator via Ant in a forked JVM process. Try setting fork to false (build.xml:271).

@dustContributor
Copy link
Author

I'm not entirely sure why but I fired up IntelliJ today and it started generating like half of the JNI.java class, a lot of "invokeXYZ" functions were missing. I just reverted it to the repo's version since that had everything but I have no clue what went wrong.

In any case:

Probably because this launches the Generator via Ant in a forked JVM process

You're right, changed the fork to false and breakpoints started working.

Is there any useful output?

It launches normally but there isn't any additional output. I mean, it launches in a separate process like you said at first, so there is no debug output (or any output) from the terminal where I launch it.

If I prevent the 'extract' tool to be forked with the flags, I can no longer pass the required arguments to the process (like clang's path). They seem to be getting ignored.

I mean, if I launch with the debugger from IntelliJ like:

ant extract

It complains that Dclang wasn't passed, if I pass it like:

ant extract -Dclang=path

It complains "-Dorg.lwjgl.llvm.clang.libname=path" wasn't passed. If I pass that like:

ant extract -Dclang=path -Dorg.lwjgl.llvm.clang.libname=path
// or
ant extract -Dclang=path -Djvmargs="-Dorg.lwjgl.llvm.clang.libname=path"

It also gets ignored, it doesn't seems to reach the Configuration class.

I also tried setting it directly from the build.xml file like

 <jvmarg value="-Dorg.lwjgl.llvm.clang.libname=path"/>

But it's still null in the program when I put a breakpoint in main.

I'm guessing the debug flags are getting ignored too.

@dustContributor
Copy link
Author

dustContributor commented Jan 6, 2019

All right. Managed to get the debugger working via adding a bunch of flags to the extract tool in build.xml (Xdebug, debugging agent and friends) then attaching IntelliJ's debugger after the ant task forked the process and adding an exception breakpoint.

With that I managed to inspect the inner exceptions on a "ExceptionInitializerError" that occurred the first time (after that the JVM just throws the NoClassDefFoundError exception if you try to extract the definitions again).

Turns out the message is:

A required function is missing: clang_Type_getObjCObjectBaseType

... But Google knows nothing about "getObjCObjectBaseType"

In any case, my libclang version is 7.0.1-1, from Debian's "testing" repositories, is that recent enough? Or am I pointing to the wrong .so?

ant extract -Dclang=/usr/lib/llvm-7/lib/libclang.so.1

EDIT: Turns out 'experimental' repositories have libclang 8, so I used that instead and now ClangIndex gets initialized correctly, but I am getting an UnsatisfiedLinkError on JNI.invokeP when ClangIndex.clang_createIndex gets called. I'm not understanding if it means there is some clang stuff missing, or if the native core of LWJGL isn't getting loaded.

EDIT2: Well turns out that even the native part of the JNI.java class got halfway generated so it was also missing a bunch of invokeXYZ functions, that's why invokeP threw an unsatisfied link error. So I recompiled that, with the complete .c file, and now my grand error message is:

о&�>�

Your tool hates me, Spasi.

@Spasi
Copy link
Member

Spasi commented Jan 7, 2019

started generating like half of the JNI.java class

The LWJGL generator gathers all native calls necessary to support the various bindings and creates a deduplicated set (based on call convention and signature) that goes into the JNI.java class. This makes the core LWJGL shared library smaller. When some bindings are disabled, the set usually becomes smaller, so that's what's happening.

Your tool hates me, Spasi.

Hmm, yeah, ant extract also uses fork="true" spawn="true". Fwiw, if I change a ClangIndex function name to something random, I can see the stacktrace in the tool's "console" panel. With spawn="false", I also get the same output on the terminal that runs ant extract.

Anyway, you can run the tool from within IntelliJ with the Template Extraction Tool run configuration. Just edit it to set the correct org.lwjgl.llvm.clang.libname. You should be able to debug normally like that.

@dustContributor
Copy link
Author

dustContributor commented Jan 7, 2019

What version of clang are you using?

EDIT:

When some bindings are disabled, the set usually becomes smaller, so that's what's happening.

So I only had LLVM/Clang bindings enabled, but the generated JNI.java class (and its native counterpart) didn't have invokeP and others in it, which is used by the LLVM/Clang bindings. Is that ok?

Anyway, you can run the tool from within IntelliJ with the Template Extraction Tool

Geezus you're right, why didn't I see it before. That launches the tool fine, if I point to Clang 7 I get the initialization error, if I point to Clang 8 I get a segfault so the debugger ain't much help there.

First segfault was in some deep Swing code, second was in a native stack frame I think. So it looks like a memory corruption thingy. Kinda weird.

What I do is:

  1. Launch it from IntelliJ pointing to libclang 8.
  2. Add dNewton, dgNewton and dgCore to the include paths.
  3. Add dgNewton/Newton.h as the main header.
  4. Click extract.

Am I doing something wrong?

EDIT2:

Even if I cant use the generator, I can try with the gist you posted earlier. So far I'm stuck on the generate part. First thing I noticed was that the order of declaration was important in the type definitions. I was getting NPEs on the callbacks since those used structs that were defined later in the file.

Once I got that sorted out I started to get "Data pointer not validated" exceptions on a few of the functions like:
https://gist.github.com/Spasi/0d064d5382ab2e037d04eca42aefa459#file-newton-kt-L1054
https://gist.github.com/Spasi/0d064d5382ab2e037d04eca42aefa459#file-newton-kt-L1062

What does it means? I can just add "Unsafe.." to them but I'm not sure what is it doing, what kind of validation is it expecting? Like a length/size parameter?

@Spasi
Copy link
Member

Spasi commented Jan 7, 2019

What version of clang are you using?

I'm using version 8, built from source (~Oct 27), on Windows.

if I point to Clang 8 I get a segfault

Please attach a crash dump, it might be helpful.

When I have time, I'll do some testing on Linux. I'll also try to make 8.0 functions optional, so that the tool can run on a stable 7 build.

For now, I have updated the gist above to the latest Newton.h. I did have some trouble parsing it, because the tool runs in C mode basically and the header had some invalid constructs (that are valid in C++). Fixed them manually and I also had to add --include=stdbool.h to the compiler arguments.

I was getting NPEs on the callbacks since those used structs that were defined later in the file.

Yes, this might happen, the tool outputs callbacks before structs. I can probably change this so that the header order is respected.

What does it means? I can just add "Unsafe.." to them but I'm not sure what is it doing, what kind of validation is it expecting? Like a length/size parameter?

Adding appropriate modifiers to pointer parameters is the most important part of creating LWJGL bindings and it's something that cannot be automated. You must carefully examine the documentation/implementation of each function and, at the very least, add the following modifiers:

  • nullable to optional pointer parameters/members. When missing, LWJGL ensures (at runtime) that such values are never NULL.
  • Check to pointer parameters/members with known length (Check(1) is very common).
  • AutoSize/AutoSizeResult to integer parameters/members that specify the size of other, pointer parameters/members or return values. Several different cases here, you can find examples in other bindings.
  • char<Encoding> to null-terminated string parameters/members (e.g. plugInPath in NewtonLoadPlugins). This is not a modifier, you simply change the char type to a more specific one.
  • Unsafe to pointer parameters/members that cannot be made safe with any of the above (e.g. ptr in NewtonFree).

@dustContributor
Copy link
Author

dustContributor commented Jan 11, 2019

Please attach a crash dump, it might be helpful.

hs_err_pid10248.log

For some reason I get a segfault if I launch it from IDEA. If I launch it from a terminal, then attach the debugger, I get garbled console output in the tool. This is testing with libllvm 8.

I'm using version 8, built from source (~Oct 27), on Windows.

I'm using libllvm 8 from revision 350193 in their SVN repo, which is ~700 commits behind current master (350916), it's what Debian 'experimental' repos have. The 'testing' repos have libllvm 7.0.1

I have updated the gist above to the latest Newton.h
I'll do some testing on Linux. I'll also try to make 8.0 functions optional, so that the tool can run on a stable 7 build.

Awesome, thanks!

Yes, this might happen, the tool outputs callbacks before structs. I can probably change this so that the header order is respected.

Yeah I think that'd be good.

Adding appropriate modifiers to pointer parameters is the most important part of creating LWJGL bindings [...] You must carefully examine the documentation/implementation of each function

Ah so you're saying I should know what I am doing, any more unreasonable requests? 😄

I have one question, when you do the 'nativeClass' extension method, the "prefixMethod" is the string that every method will be prefixed with ("Newton" in this case), but what is the plain "prefix"? Is it for constants?

@dustContributor
Copy link
Author

dustContributor commented Jan 12, 2019

I found this thing with the generator, look at this callback:

	typedef dLong (*NewtonGetTimeInMicrosencondsCallback) ();

Seems fine, no args and returns a long.

Now the generated template for this is:

val NewtonGetTimeInMicrosencondsCallback = Module.NEWTON.callback {
    long_long(
        "NewtonGetTimeInMicrosencondsCallback",
        "",
        void()
    ) {
        documentation = "Instances of this interface may be passed to the #FIXME() method."
    }
}

Which generates a callback with a void argument. Legal in C, not so legal in Java.

This has the problem that when you ask it to generate the .java files, it does this:

        @Override
        public long invoke(void *) {
            return delegate.invoke(*);
        }

Which is invalid. The C code seems fine so I'm guessing this is something the template generator has to transform? Or the template processor should not add an argument if it's void?

EDIT:
Going a bit further, it seems the tool also confuses multi dimensional arrays. There are a couple fields in Newton's C code that are like:

dFloat m_offsetMatrix[4][4]

With the tool seemingly generating:

float.p("m_offsetMatrix", "")[4]

Which causes issues later on on .java code generation, since it tries to pass a FloatBuffer to a function that doesn't accepts one when generating the accessor for that field.

I fixed it with

float("m_offsetMatrix", "")[16]

EDIT2:

Well I wanted to try out just generating bindings for one of Newton's C examples, so far I got the types and I'd need to generate bindings for a couple functions only.

I got NewtonTypes.kt working, so I wanted to try out the native compilation step before making the few function bindings in Newton.kt.

I'm not understanding the build process, is it trying to download compiled artifacts from your build server? I'm getting exceptions like:

 Can't get https://build.lwjgl.org/nightly/linux/x64/newton/libdVehicle.a to /home/user/git/lwjgl3/bin/libs/linux/x64/newton/libdVehicle.a

EDIT4:

I think I understand, I copied the build section from Bullet, and that one has a bunch of sections.

<!-- NEWTON -->
<build module="newton" simple="true" linker="g++" if:true="${binding.newton}">
    <beforeLink>
        <mkdir dir="${lib}/${platform}/${build.arch}/newton"/>
        <parallel threadsPerProcessor="2" failonany="true" unless:set="lib-uptodate">
            <update-dependency name="Newton01" artifact="${build.arch}/newton/libdMath.a"/>
            <update-dependency name="Newton02" artifact="${build.arch}/newton/libdgCore.a"/>
            <update-dependency name="Newton03" artifact="${build.arch}/newton/libdScene.a"/>
            <update-dependency name="Newton04" artifact="${build.arch}/newton/libnewton.a"/>
            <update-dependency name="Newton05" artifact="${build.arch}/newton/libdNewton.a"/>
            <update-dependency name="Newton06" artifact="${build.arch}/newton/libdVehicle.a"/>
            <update-dependency name="Newton07" artifact="${build.arch}/newton/libdgPhysics.a"/>
            <update-dependency name="Newton08" artifact="${build.arch}/newton/libdAnimation.a"/>
            <update-dependency name="Newton09" artifact="${build.arch}/newton/libdContainers.a"/>
            <update-dependency name="Newton10" artifact="${build.arch}/newton/libdCustomJoints.a"/>
            <update-dependency name="Newton11" artifact="${build.arch}/newton/libtinyxml.a"/>
        </parallel>
    </beforeLink>
    <link>
        <arg value="-ldl"/>
        <arg value="-L${lib}/${platform}/${build.arch}/newton"/>
        <arg value="-lNewton01"/>
        <arg value="-lNewton02"/>
        <arg value="-lNewton03"/>
        <arg value="-lNewton04"/>
        <arg value="-lNewton05"/>
        <arg value="-lNewton06"/>
        <arg value="-lNewton07"/>
        <arg value="-lNewton08"/>
        <arg value="-lNewton09"/>
        <arg value="-lNewton10"/>
        <arg value="-lNewton11"/>
    </link>
</build>

I'm guessing those kind of libraries are the ones you don't commit to the repo because they're too big, so you make it download the artifacts from a build server, right?

@Spasi
Copy link
Member

Spasi commented Jan 12, 2019

Which generates a callback with a void argument. Legal in C, not so legal in Java.

Not related to Java. The template DSL is supposed to look like C and it actually has some of its quirks (e.g. using (void) on functions with no arguments). This must be the first time it's occurred on a callback though. It's a minor bug and will be fixed soon. For now, simply drop void().

it seems the tool also confuses multi dimensional arrays

Thanks, will have a look.

I'm guessing those kind of libraries are the ones you don't commit to the repo because they're too big, so you make it download the artifacts from a build server, right?

Correct. Unfortunately, Bullet doesn't support shared library builds, so LWJGL links it statically. We should use Newton as a shared library if possible.

@dustContributor
Copy link
Author

dustContributor commented Jan 20, 2019

This must be the first time it's occurred on a callback though.

Literally uncharted lands 😄

We should use Newton as a shared library if possible.

Yeah it has a shared library flag and everything. But now I'm wondering, it looks like it wont be possible to build it with just the <build_simple> script, it has a bunch of CMakeLists.txt inside defining each a separate module, this means I'd have to pull all .so files from somewhere in my local file system for now.

How do I do that? <update-dependency> tries to fetch it from your build server.

Also I'm not understanding if LWJGL supports loading multiple .so/.dll for a single library, or if it expects everything to be packaged into a single dependency. Which one is it?

EDIT:

I just found out that you can make a single .so out of multiple .a with GCC, by simply sitting on the output folder and doing:

gcc -shared -o newton.so -Wl,--whole-archive libdAnimation.a libdContainers.a libdCustomJoints.a libdgCore.a libdgPhysics.a libdMath.a libdNewton.a libdScene.a libdVehicle.a libnewton.a libtinyxml.a -Wl,--no-whole-archive

I had read it before but I didn't understood what it was until Newton guy pointed it out to me. I'm not entirely sure if there is any drawback in using it that way instead of going and editing the ~10 CMakeLists.txt in Newton so it outputs a single .so, maybe some optimizations are lost in the way since it works with pre-compiled code? No idea honestly.

EDIT2:

Well I think I managed to compile something, I mean, a 1.2MB liblwjgl_newton.so file at least. I ended up with this build config:

<!-- NEWTON -->
<build module="newton" simple="true" linker="g++" if:true="${binding.newton}">
    <beforeLink>
        <mkdir dir="${lib}/${platform}/${build.arch}/newton"/>
        <copy todir="${lib}/${platform}/${build.arch}/newton" flatten="true">
            <path>
                <pathelement path="${src.main}/src/build/lib/libdMath.a"/>
                <pathelement path="${src.main}/src/build/lib/libdgCore.a"/>
                <pathelement path="${src.main}/src/build/lib/libdScene.a"/>
                <pathelement path="${src.main}/src/build/lib/libnewton.a"/>
                <pathelement path="${src.main}/src/build/lib/libdNewton.a"/>
                <pathelement path="${src.main}/src/build/lib/libdVehicle.a"/>
                <pathelement path="${src.main}/src/build/lib/libdgPhysics.a"/>
                <pathelement path="${src.main}/src/build/lib/libdAnimation.a"/>
                <pathelement path="${src.main}/src/build/lib/libdContainers.a"/>
                <pathelement path="${src.main}/src/build/lib/libdCustomJoints.a"/>
                <pathelement path="${src.main}/src/build/lib/libtinyxml.a"/>
            </path>
        </copy>
    </beforeLink>
    <link>
        <arg value="-ldl"/>
        <arg value="-L${lib}/${platform}/${build.arch}/newton"/>
        <arg value="-ldMath"/>
        <arg value="-ldgCore"/>
        <arg value="-ldScene"/>
        <arg value="-lnewton"/>
        <arg value="-ldNewton"/>
        <arg value="-ldVehicle"/>
        <arg value="-ldgPhysics"/>
        <arg value="-ldAnimation"/>
        <arg value="-ldContainers"/>
        <arg value="-ldCustomJoints"/>
        <arg value="-ltinyxml"/>
    </link>
</build>

That is, cmake && make'ing the library manually, then letting the script copy back the resulting .a, then linking them up all together into a single .so.

I noticed I wasn't getting any generated C file and I didn't realize that the libraries have a "nativeDirective" call in the template to include the required header files. So I copied Newton.h into the main/c folder, fixed two warnings, and compiled all the types plus just two random functions (WorldGetVersion and something else) to try it out.

Now, given that I have a lwjgl_newton.so, and a bunch of .class files, how do I package all of this up?

@Spasi
Copy link
Member

Spasi commented Jan 22, 2019

tries to fetch it from your build server.

If you export the LWJGL_BUILD_OFFLINE=true environment variable, the Ant build will stop looking online for anything.

I'm not understanding if LWJGL supports loading multiple .so/.dll for a single library, or if it expects everything to be packaged into a single dependency. Which one is it?

Multiple shared libraries for a single binding should be fine. Currently the LLVM/Clang bindings work like that. The CUDA bindings will also use multiple shared libraries soon, when more CUDA Toolkit libraries are supported.

In case multiple shared libraries need to be bundled with LWJGL, the release-module target must be configured with a custom natives definition (examples: GLFW, OpenAL, OpenVR).

you can make a single .so out of multiple .a with GCC

This is the preferred solution. Should be done for all platforms in CI, then LWJGL simply downloads and bundles a single shared library (like bgfx, GLFW, OpenAL, etc.).

Well I think I managed to compile something [...] how do I package all of this up?

This is different from using a shared library and calling it via org.lwjgl.system.JNI (the preferred solution I mentioned above). Sounds like it's exactly the same setup as Bullet, so use that as a reference (search for binding.bullet in the Ant scripts).

Just to be clear:

  • Solution A: CI builds a shared library that exports all Newton functions. The LWJGL build downloads and bundles the shared library. At runtime, LWJGL loads the shared library and the function addresses, calls them via org.lwjgl.system.JNI. There is no generated C code in this case (i.e. no liblwjgl_newton.so, just a libnewton.so) and you don't need to add the Newton headers anywhere.

  • Solution B: CI builds a bunch of static libraries. LWJGL downloads the static libraries, links them to a shared library that exports JNI functions (that call the corresponding Newton functions). There is generated C code in this case (so you need the Newton headers and appropriate nativeDirective calls) and there should be a liblwjgl_newton.so that will be used by LWJGL at runtime.

The first solution is very simple to configure in LWJGL, but you'll need to worry about bundling the static libraries as a shared library in the CI scripts. Doing this for all 3 platforms and making sure the functions are properly exported (see _NEWTON_BUILD_DLL in Newton's header, is there a CMake option that sets it?) might be a bit of work.

@dustContributor
Copy link
Author

So I found the release target while searching for binding.bullet but it fails with:

[module-info-gen] core: OK
[module-info-gen] core.natives: OK
[module-info-gen] nuklear: FAILED
[module-info-gen] compiler.err.package.empty.or.not.found
[module-info-gen] ERROR
[module-info-gen] 180
[module-info-gen] 171
[module-info-gen] 187
[module-info-gen] org.lwjgl.generator.util.ModuleInfoGen$1MemoryJavaFileObject[file:///home/user/git/lwjgl3/modules/lwjgl/nuklear/src/main/java/module-info.java]
[module-info-gen] package is empty or does not exist: org.lwjgl.newton

The only binding I (think) have enabled is Newton. I dont understand why it says "nuklear: FAILED", it isn't even compiled.

I have no clue why it says the "package is empty or does not exist" either. The Newton .class files are generated and with that exact package... Although I don't see a module-info.class in the Newton folder inside bin now that I think of it. I added it in newton/src/resources/module-java.info though.

I was launching the ant generate/compile/etc tasks with Java 8 as JAVA_HOME, should I have used Java 11?

@Spasi
Copy link
Member

Spasi commented Mar 18, 2019

I am not entirely sure why it didn't pop up before since I never touched the "FIXME" links.

Callbacks are evaluated in a final step after the generation of all bindings has succeeded. Most likely, you were hitting errors earlier in the process and never got to generating callbacks before.

So, I'll be waiting your evaluation on how the Newton bindings feel. My biggest worry is that there are too many callback types and upcalls are super-slow via JNI (at least an order of magnitude slower than downcalls). I'm not familiar with Newton's API, but if the number of callback invocations depends on scene complexity, JNI overhead will probably limit scalability.

@dustContributor
Copy link
Author

Most likely, you were hitting errors earlier in the process and never got to generating callbacks before

Yeah, now that you mention it, you're probably right.

My biggest worry is that there are too many callback types and upcalls are super-slow via JNI

It's true, but I don't think it can be helped. AFAIK most physics engines do most of their contact/collision/intersection events through callbacks, I don't think Newton is "special" in that regard.

@Zamundaaa
Copy link

So would it be possible to include Newton in the latest nightly or provide other means of download?
I'd very much appreciate being able to test it, too.

@dustContributor
Copy link
Author

I got new developments 😄

https://gfycat.com/happygoluckyaltruisticanemonecrab

This is using the few implemented Newton functions in my LWJGL fork. Apparently the "apply force and torque" callback is called for all bodies every "tick" in Newton, unless the body is static (ie, mass 0). Kinda curious too that you have to apply gravity in said callback "manually", instead of having a "NewtonSetWorldGravity(long handle, float gravity)" like you'd have in other libraries.

I guess it could impact performance like Spasi said.

Anyway, next milestone is implementing a 'kinematic' body for the "player", that is, a body that is moved by forces outside the simulation that still can collide and stuff.

So would it be possible to include Newton in the latest nightly or provide other means of download?

You could try to clone my LWJGL fork I guess... Then try to build it. This isn't something Spasi is upstreaming, rather it's just stuff I am doing on my own LWJGL fork so Spasi can keep working on The Great Plan undisturbed by my incessant questions.

@dustContributor
Copy link
Author

Hey @Spasi I have some issues, I am trying to map this function:

void* NewtonWorldAddListener (const NewtonWorld* const newtonWorld, const char* const nameId, void* const listenerUserData)

Afaik the void* listenerUserData is an unsafe pointer.

The char* nameId apparently is a fixed length string. I mean, Newton inside copies it back, and that m_name has a fixed length . It isn't in the docs, what's the approach to take here? Check(32)..charUTF8.const.p? Unsafe..charUTF8.const.p?

In any case, even if I add those, it complains on the sizing of the returned pointer:

    void.p(
        "WorldAddListener",
        "",

        NewtonWorld.const.p.const("newtonWorld", ""),
        Check(32)..charUTF8.const.p.const("nameId", ""),
        Unsafe..void.p.const("listenerUserData", "")
    )

Gives:

Caused by: java.lang.IllegalStateException: No AutoSizeResult parameter could be found.

That's an opaque pointer I think, and I have no clue how to tell LWJGL not to worry about it. I CTRL+F'd around for a bit and I haven't found templates that does this.

@octylFractal
Copy link
Contributor

NanoVG's ctx pointer is opaque, and the template works like this:

val NVGcontext = "NVGcontext".opaque

... 

val ctx = NVGcontext.p

@dustContributor
Copy link
Author

dustContributor commented Apr 26, 2019

That's a type definition, not a function definition. I am trying to define a function, as you can see in the code I wrote.

EDIT: Unless I'm missing something I've been looking over 15 bindings and nothing has an un-sized void* as a return value 🤔

@octylFractal
Copy link
Contributor

This case is similar, though I guess not enough perhaps. This makes a proper opaque return for the NVGContext struct. See the return usage at https://github.com/LWJGL/lwjgl3/blob/master/modules/lwjgl/nanovg/src/templates/kotlin/nanovg/templates/nanovg_gl2.kt#L61.

The corresponding C for that function is: NVGcontext* nvgCreateGL2(int flags);, and NVGContext is
simply typedef struct NVGcontext NVGcontext;.

I would think you can do something similar with void by doing "void".opaque, but perhaps the lack of size breaks something. Though, considering that it's a pointer, why does size matter? The pointer size is always the same.

@dustContributor
Copy link
Author

Well using "void".opaque works on the return type, it generates this on the native side:

JNIEXPORT jlong JNICALL JavaCritical_org_lwjgl_newton_Newton_nNewtonWorldAddListener(jlong newtonWorldAddress, jlong nameIdAddress, jlong listenerUserDataAddress) {
    NewtonWorld const * const newtonWorld = (NewtonWorld const * const)(intptr_t)newtonWorldAddress;
    char const * const nameId = (char const * const)(intptr_t)nameIdAddress;
    void * const listenerUserData = (void * const)(intptr_t)listenerUserDataAddress;
    return (jlong)(intptr_t)NewtonWorldAddListener(newtonWorld, nameId, listenerUserData);
}

I guess that would work, both on 32 and 64 bit, but I'm not sure if it's how it should be used. In any case, that's enough for trying stuff out stuff, thanks!

@octylFractal
Copy link
Contributor

That looks correct, given the output of the nvgCreateGL2 example: https://github.com/LWJGL/lwjgl3/blob/master/modules/lwjgl/nanovg/src/generated/c/org_lwjgl_nanovg_NanoVGGL2.c#L38

@dustContributor
Copy link
Author

Cool! I also used it for the functions that expected the void* listener as a parameter, since LWJGL would generate a ByteBuffer parameter with Unsafe..void.p which doesn't really makes sense in this context.

@Spasi
Copy link
Member

Spasi commented Apr 26, 2019

Hey @dustContributor,

The char* nameId apparently is a fixed length string. I mean, Newton inside copies it back, and that m_name has a fixed length . It isn't in the docs, what's the approach to take here? Check(32)..charUTF8.const.p? Unsafe..charUTF8.const.p?

No, use charUTF8.const.p.const without a modifier, i.e. a simple null-terminated string. It's safe because the 32 value passed to strncpy is the maximum string size. The copy will stop when it encounters a \0 or when it reaches 32 characters, whichever comes first.

Check(32) would make the function require a string with at-least 32 characters, which is wrong. Unsafe would simply remove the null-termination check, which is... unsafe.

That's an opaque pointer I think, and I have no clue how to tell LWJGL not to worry about it

There's opaque_p in GlobalTypes, which is just a reusable "void".opaque.p. Use it like this:

opaque_p(
    "WorldAddListener",
    "",

    NewtonWorld.const.p.const("newtonWorld", ""),
    charUTF8.const.p.const("nameId", ""),
    opaque_p.const("listenerUserData", "")
)

@dustContributor
Copy link
Author

There's opaque_p in GlobalTypes

Ah right, I think I've seen it before but I forgot about it.

use charUTF8.const.p.const without a modifier, i.e. a simple null-terminated string

Makes sense, thanks!

@dustContributor
Copy link
Author

I hit an issue when adding a bunch of functions:

Error:

[Linker] /usr/bin/ld: /tmp/ccekMsSv.ltrans0.ltrans.o: in function `JavaCritical_org_lwjgl_newton_Newton_nNewtonHingeCalculateStopAlpha':
[Linker] <artificial>:(.text+0x601): undefined reference to `NewtonHingeCalculateStopAlpha'

Function definition in Newton.h

NEWTON_API dFloat NewtonHingeCalculateStopAlpha (const NewtonJoint* const hinge, const NewtonHingeSliderUpdateDesc* const desc, dFloat angle);

Binding in templates/Newton.kt

    float(
        "HingeCalculateStopAlpha",
        "",
        NewtonJoint.const.p.const("hinge", ""),
        NewtonHingeSliderUpdateDesc.const.p.const("desc", ""),
        float("angle", "")
    )

Generated native glue in org_lwjgl_newton_Newton.c

JNIEXPORT jfloat JNICALL JavaCritical_org_lwjgl_newton_Newton_nNewtonHingeCalculateStopAlpha(jlong hingeAddress, jlong descAddress, jfloat angle) {
    NewtonJoint const * const hinge = (NewtonJoint const * const)(intptr_t)hingeAddress;
    NewtonHingeSliderUpdateDesc const * const desc = (NewtonHingeSliderUpdateDesc const * const)(intptr_t)descAddress;
    return (jfloat)NewtonHingeCalculateStopAlpha(hinge, desc, angle);
}
JNIEXPORT jfloat JNICALL Java_org_lwjgl_newton_Newton_nNewtonHingeCalculateStopAlpha(JNIEnv *__env, jclass clazz, jlong hingeAddress, jlong descAddress, jfloat angle) {
    UNUSED_PARAMS(__env, clazz)
    return JavaCritical_org_lwjgl_newton_Newton_nNewtonHingeCalculateStopAlpha(hingeAddress, descAddress, angle);
}

I don't understand, the function looks ok, the parameters look ok, the generated glue looks ok. I have the same issue with 3-4 more functions.

On a side note, after using it for a bit I've realized that a whole bunch of Newton code isn't and probably wont be exposed to Java.

There is the core API, afaik defined in dgNewton, dgPhysics, dgCore, and probably dMath and dNewton although I'm not sure. This is exposed as C functions through Newton.h, but there are a lot of side modules (dVehicle, dAnimation, dCustomJoints, dScene, etc) that look more like convenience C++ classes that use the base C API to do a bunch of things.

This means I don't think we'd really need most of the dXYZ libraries linked into the main .so for the base C API to work, this is probably what the CMake 'NEWTON_BUILD_CORE_ONLY' flag does.

@Spasi
Copy link
Member

Spasi commented May 1, 2019

Looks like there's no implementation for this function in Newton.cpp. Better report this to Newton's developers.

@dustContributor
Copy link
Author

Duh it didn't occur to me to look at the .cpp, thanks, I'll comment them out for now then.

@dustContributor
Copy link
Author

Spasi, I have a question about callbacks. I have a call like this:

var contacts = NewtonWorldConvexCast(newtonWorld,
		mat4Buffer,
		vec3Buffer,
		collider,
		float1Buffer,
		MemoryUtil.NULL,
		(body, shape, u) -> owner.body == body ? 0 : 1, // Capturing lambda
		contactBuffer,
		0);

The capturing lambda is a callback with the signature (long, long, long) -> int. It's a filter for the cast call, so the filter is run for each body colliding with the convex shape, and returns if it should be included in the resulting contact list or not.

If I do that inside a loop, LWJGL will allocate in native memory a new callback every time right? I mean, those have to be freed manually? Is there a way to stack allocate them? Or this isn't the case?

@Spasi
Copy link
Member

Spasi commented May 21, 2019

If I do that inside a loop, LWJGL will allocate in native memory a new callback every time right?

Yes.

I mean, those have to be freed manually?

Yes. If the debug allocator is enabled, it will tell you if you're leaking callbacks.

Is there a way to stack allocate them?

No. Callbacks must live in executable memory.

The best approach is to instantiate (and reuse) a callback instance instead of a lambda. Note that each callback type is mapped to a CBTypeI interface and a CBType abstract class. You can create a CBType instance using either an anonymous inner class or the CBType.create(<lambda>) factory method.

You can use the userData parameter to "capture" per-invocation data. You could also use Java-based solutions (ThreadLocal?) if that's not enough.

@dustContributor
Copy link
Author

dustContributor commented May 26, 2019

I see, thanks! Do ThreadLocals work if the callbacks are called from threads created on the native side? That is, do they get run under a JVM thread regardless when transitioning back to Java-land?

Another thing I am wondering is that unless I am misunderstanding something, there isn't a way to reuse callback instances without giving up the "type safe" call.

For instance, if I have a signature like Lib#LibMethod(FloatBuffer, FloatBuffer, SomeCallbackI) where both FloatBuffers are checked so they can hold 3 floats and are non-null.

Whatever I pass as SomeCallbackI instance, it calls SomeCallbackI#address inside that LibMethod function, then delegates to nLibMethod. That 'address' call will in turn call Callback#create, which will allocate a new executable callback right?

So if I want to have a reusable callback, I have to do it like:

// In a constructor/init method/somewhere
this.callbackAddress = ((SomeCallbackI)(a) -> doStuff(a)).address();
// then when calling the native method in a loop
Lib.nLibMethod(memAddress(fba), memAddress(fbb), this.callbackAddress);

So I got my reusable callback, but I had to give up the automatic null/size checks for those float buffers since I am using the 'n' version of the method.

Is this is how its done? Or am I missing something?

@Spasi
Copy link
Member

Spasi commented May 26, 2019

Do ThreadLocals work if the callbacks are called from threads created on the native side? That is, do they get run under a JVM thread regardless when transitioning back to Java-land?

Foreign threads that attach (dynamically and asynchronously) to the JVM are mapped to a java.lang.Thread and ThreadLocal should work. But I'm not sure how that would be useful.

I was talking about calling a function from Java that does some work and triggers callbacks back into Java. Those callbacks will be invoked on the same thread that initiated the action (a good example is glfwPollEvents). In such a case, you could prepare a ThreadLocal before the call and it'll have the expected value when read from inside the callback.

there isn't a way to reuse callback instances without giving up the "type safe" call

There is a way, I have explained how in my previous reply:

The best approach is to instantiate (and reuse) a callback instance instead of a lambda. Note that each callback type is mapped to a CBTypeI interface and a CBType abstract class. You can create a CBType instance using either an anonymous inner class or the CBType.create(<lambda>) factory method.

Using your example:

SomeCallback cb = SomeCallback.create(a -> doStuff(a)); // reusable
Lib.LibMethod(fba, fbb, cb);

SomeCallback implements SomeCallbackI and the above works. Calling .address() on SomeCallback does not create a new callback every time.

@dustContributor
Copy link
Author

Ahh right, I didn't see that Callback#address override that just returns the long field, makes sense, thanks!

@Alex-----
Copy link

There's another bullet C wrapper here: https://github.com/AndresTraks/BulletSharpPInvoke/tree/master/libbulletc that is a lot closer to the C++ API. I don't know if it is better than Newton or not.

@Spasi
Copy link
Member

Spasi commented Aug 31, 2019

We now have another version of the Bullet bindings (libbulletc #480). The API is a straightforward wrapper around the C++ API, which is as bad as it sounds, but still better than the "official" client/server C API. It should be available for testing in the first snapshots after the 3.2.3 release.

Interestingly, like Newton, the C++ API has too many callbacks. Newton has 54 callback types and it looks like Bullet has more than that. Also, you end up calling tons of trivial C++ methods (e.g. btPersistentManifold_getContactPoint), whereas in a sanely designed C API you'd simply get a buffer back and iterate in the managed language.

JNI overhead is going to hurt.

So, yeah, it doesn't look good on the physics front. We'll probably have bindings for both Bullet and Newton soon, to gather user feedback, but I'm still open to better suggestions.

@Spasi
Copy link
Member

Spasi commented Oct 20, 2021

To anyone still looking forward to this, I'm sorry to say that I've removed the Bullet Physics bindings from LWJGL. I was not happy with it technically (see discussion above) and there is simply not enough maintenance "budget" available to be dealing with such an awkward library.

Alternative solutions:

  • Libbulletjme, seems to be actively maintained.
  • If you're feeling adventurous, create your own bindings on top of the C++ API, using JavaCPP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants