filament icon indicating copy to clipboard operation
filament copied to clipboard

Provide a C library wrapper for filament

Open shartte opened this issue 6 years ago • 23 comments

Is your feature request related to a problem? Please describe. Use filament from other languages such as .NET, Python, or pretty much anything with a foreign function interface. C++ ABIs are vendor specific, while C can be portable.

Describe the solution you'd like Provide a shared library that exposes a C ABI that is not necessarily stable or backwards compatible, but allows use of filament from other languages. It would be aligned closely to the JNI wrapper, but would not make use of any language specific environment (so no JVM, etc.). Instead, it would expose it's interface using basic C types, and wrap native Filament pointers.

Describe alternatives you've considered For .NET, there's C++/CLI which would allow a wrapper to be written in C++, but P/Invoke is more portable, since .NET Core does not support the various forms of C++/CLI. Also, a C based wrapper library would allow languages other than .NET to make use of filament. Languages like Python for example, which could then make use of filament through cffi (which also has more optimal integration in Python JITs such as PyPy). I have not looked at other languages but FFI is not uncommon. Currently Java will only allow FFI through wrappers such as JNA (not as fast as JNI), but upcoming developments in the Java world will even allow Java to make use of the C interface (Project Panama or today via JNR), although it's unlikely this'll make it to Android. As such on Android, JNI will probably stay relevant for the forseeable future.

I've also looked at generating C-wrappers declaratively through software like SWIG, but apparently there is no working C backend for SWIG at the moment, which is unfortunate.

What I didn't investigate is writing a custom wrapper generator, due to the (seemingly) higher maintenance cost.

OS and backend Not OS specific.

shartte avatar Aug 24 '18 17:08 shartte

I've opened this issue here since I have an around 90% complete C wrapper based on the JNI code that I am already using from .NET successfully. I am unsure on how to proceed though since there's certainly some design discussion to be had around a C interface (naming schemes, etc.), and I am unsure whether to have that in an issue such as this, or in a PR straight away.

shartte avatar Aug 24 '18 17:08 shartte

The JNI layer in android/filament-android/src/main/cpp is effectively a C wrapper already. It could be refactored into a separate standalone C wrapper that would be invoked by JNI.

romainguy avatar Aug 24 '18 17:08 romainguy

What would your desired naming scheme for symbols be (if any)? Since there aren't any namespaces, should all functions be prefixed, and which naming scheme should be used? Right now I have EntityManager_CreateEntities, which wraps EntityManager::create for multiple entities to create. I guess there are a lot of different possible names for this function:

  • Leave it as it is
  • em_create_entities
  • entitymanager_create_entities
  • fl_em_create_entities
  • flm_em_create_entities
  • flm_entitymanager_create_entities
  • and so on

What would your preference be?

shartte avatar Aug 24 '18 18:08 shartte

I must admit I don't have much experience as a C developer, I would be inclined to go with a lower case variant sur as fl_em_create_entities. It seems useful to have part of the prefix be global to Filament.

romainguy avatar Aug 24 '18 19:08 romainguy

I think we should use the Android NDK convention, so something like EntityManager_Create(), but we need a namespace too. Personally, I would go with explicit names like Filament_EntityManager_Create() or Filament_VertexBuffer_Builder_attribute().

e.g.

Filament_VertexBuffer_BuilderHandle builder = Filament_VertexBuffer_Builder();
Filament_VertexBuffer_Builder_attribute(builder, ... );
Filament_VertexBufferHandle vb = Filament_VertexBuffer_Builder_build(engine);

Alternatively, I could go with Flmt instead of Filament.

For overloaded methods, I would just avoid them entirely and only expose the most full featured one.

pixelflinger avatar Aug 24 '18 20:08 pixelflinger

Would love to hear any progress on this, and I can help with interop for .NET.

nxrighthere avatar Oct 10 '18 22:10 nxrighthere

Any news on this?

bhack avatar Feb 28 '19 02:02 bhack

Not at the moment unfortunately no. It hasn't been a priority so far. Ideally we'd want a way to generate bindings to avoid maintaining them manually for C, JavaScript and Java/JNI.

romainguy avatar Feb 28 '19 06:02 romainguy

If you don't want to maintain the C API you still need to use Javacpp, Pybind11 and Emscripten and other solution for other languages to bind c++. Currently you are using a C wrapper for java/Android https://github.com/google/filament/issues/139#issuecomment-415832016, Emscripten for Javascript and rejected to integrate the pybind11 PR for python here (now it is unmaintaimed in an external repository). So what is the strategy?

bhack avatar Feb 28 '19 07:02 bhack

We currently maintain the Java and JavaScript bindings manually because the bulk of the work has already been done but adding other languages would require a more automated way of doing this (and ideally a single solution).

romainguy avatar Feb 28 '19 15:02 romainguy

Yes but I don't think that a single solution exist from c++. If exist it will be the holy grail of c++ library bindings. It is why projects like Javacpp or Pybind11 was created. But if you know a single solution from c++.. Also one of the most famous Google project like Tensorflow needs to create wrapper over a "small" C API core.

bhack avatar Feb 28 '19 16:02 bhack

One of the reasons I stopped my automatic generation project is actually that the destructor doesn't really destruct things... You need to call engine->destroy() so actually transferring this pattern to a managed language automatically is just hard.

shartte avatar Feb 28 '19 16:02 shartte

@shaartte I wouldn't let that stop your work. The Java Finalize method was deprecated in Java 9 due to its unreliable nature (won't happen until GC and may not happen at all at program shutdown). IMO, the C API should be as close to the C++ API as possible and language specific libraries that follow the idioms of the language should be outside the scope of the automatic generation project project.

AfraidKnot avatar Mar 01 '19 17:03 AfraidKnot

@AfraidKnot Finalizer issues are one of the reasons why our Java bindings require explicit destruction. Android internally has better way of doing this but they haven't been exposed as public APIs yet.

romainguy avatar Mar 01 '19 17:03 romainguy

At least from the .NET side, the correct way for destroying would be to make use of IDisposable implementations or SafeHandles.

pjmlp avatar Mar 04 '19 09:03 pjmlp

It seems that upcoming .NET 5 will bring Java interop. Not sure if this will help us anyhow rather than providing a proper C interface, but yea...

nxrighthere avatar May 08 '19 19:05 nxrighthere

If the only purpose of the C interface is to provide something that wrappers can target, AND it's good to seek a method that is auto-generated, is there any reason to not use SWIG and skip the C-interface intermediary? I know SWIG is considered to be kind of unsexy, but it really handles the job very well of "automatically bind a C++ library to C#/.NET/Python/JVM/* without maintainer effort"

I've used it in the past on a similar project and the use case described here for the C interface seems like two steps: "Maintain the C api, then use the C api to autogenerate P/Invoke or cffi". But SWIG does that already, but without needing to use C as an intermediary (or rather, it automatically generates the C api needed to be optimal on the target output binding).

Steve132 avatar Oct 02 '19 08:10 Steve132

@Steve132 Personally I feel like it's a bit heavy handed and also, as with most machine generated things, it's very difficult to match the quality of hand written code.

However, perhaps the biggest roadblock is the design of the API for filament. I started writing a game in C++ using filament and then started writing a C api for that game and found that I was basically just writing a C api for filament. In that process, I found that the API is difficult to work with because of the way everything is constructed around an engine instance and other manager instances.

This isn't being critical of filament by the way, I'm just stating the difficulties presented. The filament API is actually very simple and is designed to greatly lower the adoption curve and in my opinion, is very effective and successful at that. But it also imposes a state that must be maintained and becomes cumbersome when trying to stuff through a C api.

If the API were re-worked so that you just create everything out of a constructed Engine instance, this work would be greatly simplified and so would memory/lifetime management. But we're not talking about overhauling the API here.

TechnikEmpire avatar Sep 13 '20 22:09 TechnikEmpire

@TechnikEmpire Could you explain a bit more what you didn't enjoy with the API? From the Java side we effectively talk to Filment via a C API (JNI) and the only state is the Engine instance (which is the "context" equivalent you find in a lot of C APIs).

romainguy avatar Sep 13 '20 23:09 romainguy

@romainguy It's been a bit since I've visited that project of mine, so I may be miss-communicating some things. Apologies. I suppose for me it's more the issue of preventing user error than the API signatures/state being burdensome. For example, storing opaque pointers to these things and giving them back blind to the user when they're construction and destruction dependent on another object instance. Thinking about it more, it was just managing the dependent relationships while handing over opaque pointers to the user that I was agonizing about.

TechnikEmpire avatar Sep 13 '20 23:09 TechnikEmpire

I have extensive experience with swig and I strongly disagree about it being too heavyweight. In terms of bindings to python or other dynamic languages, even hand-coded bindings will be equivlantly slow.

Something that exists and requires little maintenance is superior to something that either does not exist or quickly becomes obsolete.

Steve132 avatar Sep 15 '20 06:09 Steve132

+1 for python

Zumbalamambo avatar Nov 01 '20 23:11 Zumbalamambo

@romainguy Take a look here:

https://github.com/Mizux/cmake-swig

Is this the kind of route you'd want to go? IMO it's bloat and increased maintenance to actually auto-generate for every language people will want. It may be simple to just auto-gen the C-API alone and let external projects build bindings. As you can see from reference project, there is an overhead that comes with every supported language. But that's just my opinion, your call. Looking for your input before I invest any time investigating.

TechnikEmpire avatar Mar 20 '21 16:03 TechnikEmpire

I don't think this will happen any time soon.

pixelflinger avatar Aug 16 '22 20:08 pixelflinger